Skip to content

Latest commit

 

History

History
150 lines (102 loc) · 13.7 KB

File metadata and controls

150 lines (102 loc) · 13.7 KB

AGENTS.md

This file provides guidance to AI agents when working with code in this repository.

What this is

Frontfire 2 is a standalone web frontend toolkit (UI widgets, styles, layout utilities). It has no external dependencies and no npm toolchain — there is no package.json. It is written in modern JavaScript (ES2020) and Sass, targeting recent browsers only (no transpilation, no polyfills).

The library comes in two layers:

  • Core — a jQuery replacement built on the ArrayList collection class.
  • UI — widget plugins built on Core via its plugin mechanism. Shipped as two bundles: Minimal and Complete (Complete is a superset).

The core philosophy of Frontfire Core is simplified DOM access for one or multiple nodes simultaneously with an API close to DOM (unlike jQuery that invented all new names, albeit sometimes shorter). Most of the time, the same DOM code should still work when wrapped in F(). Notable exceptions are .on() and .off() that extend addEventListener/removeEventListener's behaviour. The shorthand function F.c("name") replaces the longer document.createElement("name").

Building

Builds are produced by Mini Web Compiler (a Windows GUI app, v2.3.0+), not a CLI/npm script. It orchestrates sass, csso, rollup, and uglifyjs. The set of files to build is declared in miniwebcompiler.json. When set up and running in the background, it detects file changes and triggers a build automatically.

  • Build outputs land in src/css/build/ and src/js/build/ (these dirs are excluded from VS Code search and from jsconfig.json).
  • miniwebcompiler.cmd is a post-build hook that copies build outputs into other local projects on the developer's machine — irrelevant to library logic.
  • pack.cmd (a PowerShell/C# polyglot script) updates the version and year across all source/doc files, creates the release archives in dist/, and refreshes file sizes in readme.md.
  • Building is not required to use the library — only to produce distribution files.

The single source of truth for the version number is the first Version X.Y.Z heading in doc/changes.html; pack.cmd propagates it everywhere.

Tests

Tests are standalone HTML pages in test/, opened directly in a browser. test/frontfire-core-tests.js defines testAll() and per-area functions (testCollection(), testDom(), …) that return true/false and log failures to the console. There is no test runner or CI test step.

Architecture

Entry files and build directives

Each bundle/variant has a thin entry file in src/js/ and src/css/ (e.g. src/js/frontfire-ui-complete.js, src/js/frontfire-core-singlefile.js). Entry files contain only import statements plus special comment directives read by Mini Web Compiler:

  • /* iife-params(...) */ / /* iife-args(...) */ — IIFE wrapper parameter/argument names.
  • /* build-dir(build) */ — output subdirectory.
  • /* no-bundle-suffix */, /* no-iife */ — build flags.

The *-singlefile.js entries bundle all dependencies into one file; the non-singlefile entries expect dependencies to be loaded separately.

Core (src/js/frontfire-core.js, src/js/arraylist.js)

Frontfire is a class that extends ArrayList (a custom array-like collection of DOM nodes, not native Array). It is exposed as window.Frontfire with the short alias window.F. DOM elements expose a .F accessor returning a Frontfire instance wrapping that element.

Note the convention: frontfire-core.js itself never uses .F (it may be overwritten by host code); Frontfire UI plugins do use .F freely.

Plugin system

UI widgets live in src/js/plugins/ (one file per widget) with matching styles in src/css/plugins/. A plugin registers itself with:

F.registerPlugin("pluginName", createFn, {
    defaultOptions: { ... },   // only these keys are read from data-opt-* attributes
    methods: { ... },          // extra methods/getters on the plugin function
    selectors: [ ... ]          // CSS selectors for autostart
});

For this self-registration to take effect, a plugin file must be imported in the corresponding entry file. It will not be regarded by the build by its pure existence.

Registration defines pluginName as a property getter on the Frontfire prototype, so element.F.pluginName(options) invokes the widget.

Beyond that activation call, pluginName is a callable function object: element.F.pluginName.method() and element.F.pluginName.property (getter/setter) reach the plugin's methods and properties. All of these are bound to the calling element(s), so they implicitly know what to operate on. Except for events, this is how all code interacts with Frontfire plugins.

F.runAutostart() (called at the end of each UI bundle entry file) applies all registered plugins to document.body, matching the registered selectors. Elements with the no-frontfire CSS class (and their descendants) are skipped. Plugins registered after the first autostart are applied immediately. The UI script is expected to be placed at the end of <body>.

Per-instance/per-plugin options are resolved with F.initOptions(pluginName, element, defaults, options), which merges plugin defaults, data-opt-* HTML attributes, and passed-in options. These options instances are also used to make internal plugin data and functions available through public plugin methods or properties.

CSS

Sass sources, with frontfire-ui-complete.scss / frontfire-ui-minimal.scss as entry points (@import, not @use — see the note in the scss header about license-comment placement). In v2, nearly all former Sass variables were converted to CSS custom properties, so consumers theme the library at runtime from their own stylesheets rather than rebuilding.

Documentation

Authoritative API docs are the HTML files in doc/ (one per module/widget). doc/changes.html is the change log.

Writing doc pages

Each doc page is a standalone HTML file that loads the built library from ../src/css/build/ and ../src/js/build/.

Live examples and their displayed source code are the same element: a <div class="example no-frontfire"> whose innerHTML is extracted by doc/res/doc.js and rendered as a syntax-highlighted code block directly below the live demo. Therefore, example HTML and <script> blocks must be written exactly as they should appear in the shown source — no separate code snippets. Optional attributes on the example div:

  • data-source-highlight — regex applied to the extracted source to highlight terms of interest.
  • data-find / data-replace — regex find/replace applied to the displayed source (e.g. reformatting long attribute lists across lines or removing auto-generated live HTML from the sample).

The no-frontfire CSS class only prevents automatic activation of widgets when loading the page; first the source code will be extracted for the live view, then widgets will be activated, as this modifies the DOM tree and shall not be visible in the code sample. When activating a plugin through code in a <script> block, this cannot be deferred automatically. Therefore, enclode such calls in an F.onReady call like following. The /*hideline*/ comment will exclude this infrastructure line from the code sample. In this case, the target element must not contain the CSS class that would match the plugin's selector to activate it automatically, as it would defeat the late activation.

/*hideline*/F.onReady(() => {
F("#elementId").pluginName(...);
/*hideline*/});

TOC entries are generated automatically from the page headings; add data-aliases="word1 word2" on a .member div to make it findable under alternative terms in the TOC search. Headings with the no-toc class are excluded from the TOC. Anchor names for members shall be the same as the member name; only find alternatives when duplicate.

All prose in doc files is written one sentence per line (HTML ignores the line breaks when rendering) to keep diffs readable. Don't use bold text for highlighting words like you're used to do; the headings will do.

When adding multiple buttons in code samples, e.g. for change actions, place them in a <div class="buttons p"> instead of a regular <p> for optimal spacing.

API signatures are written as <code>method(param)</code> → ReturnType in a <ul> under the member heading; multiple overloads get separate <li> items. → this specifically means the calling Frontfire instance is returned (enabling chaining), as distinct from → Frontfire for a new instance.

New public CSS classes must be added as members to doc/frontfire-ui-classes.html; new CSS custom properties go into doc/frontfire-ui-variables.html. Cross-link related entries with See also: lines in both directions. Reference CSS classes inline as <code class="css-class">name</code> and custom properties as <code class="css-var">--name</code>. Plugin doc pages link to their variables collectively as <a href="frontfire-ui-variables.html?s=@pluginname">CSS variables</a> (the ?s= value pre-fills the search filter).

Coding guidelines

  • Tabs for indentation (match existing files).
  • Braces for single-line bodys of conditions are optional. If any branch in an if statement needs or has braces around the body, add them to all other branches as well. Always add braces around the body of other types (loops etc.).
  • Code symbols (functions, variables etc.), log messages and exception messages in English, user-facing strings and input validation messages in German at least, including in the same statement. Consider localisation selection based on the documentElement's lang attribute or an option value.
  • When adding new functions, sections or similar to a set where existing entries are sorted in alphabetical order, also insert new elements in alphabetical order. When renaming elements, move them to maintain the sort order.
  • Never use EN-DASH (U+2013), EM-DASH (U+2014) or MINUS (U+2212) in code comments; find other ways to phrase it, e.g. using colon, semicolon or a new sentence and use a regular hyphen for maths or exceptional cases.
  • Try to limit line length to 100 chars, with tab indentation of 4 columns. Consider it even more when exceeding 120 chars.

Comment policy

  • Comments in English only.
  • Wrap comments at 100 chars, with tab indentation of 4 columns, if the comment is multiple lines long. Single-line comments up to 120 chars are acceptable inside a body. Exceeding that makes side-by-side comparisons harder to read.
  • Preserve existing comments verbatim unless your own change makes them incorrect.
  • Don't compare with a previous version of the code, only mention what is there now.
  • When mentioning a code symbol (e.g. an enum flag like "Complete" in PascalCase) that happens to be an adjective or other existing word, preserve the symbol's casing for differentiation.
  • Magic values:
    • A literal a reader cannot name gets its name, not a sentence: 0x58 // RgbColor. If the value appears in several places, consider giving it (and related values) a named constant instead of a comment.
    • A literal needs a reason only when a reader cannot tell from nearby code whether it is right: a margin or timing threshold, a value that must match something outside this file, or an exception carved out of a range.
    • A size or count whose only job is to fit its content needs neither; if it is wrong, the result shows it.
    • When the non-obvious part is the approach rather than the number, that is a rejected alternative, not a magic value.
  • When you find a comment that already disagrees with the code, treat it as a bug to report, not a wording problem to fix silently. Possibly the code is wrong.
  • When you find commented-out code that would need to be updated with your changes, report it.
  • Do not invent a rationale to fill a comment; if the reason is unknown, state only what is known.

Comments inside a body

  • Only comment code whose purpose isn't obvious from naming.
  • One short line per comment; no full sentences, no trailing periods.
  • A comment that carries a reason rather than a label may run longer and be written as sentences, with normal punctuation.
  • Never restate what the code does ("// increment counter").
  • Don't add comments to code that had none, unless the logic is non-obvious.
  • A comment explaining an absence has nothing to attach to, so it has to be phrased as a statement about what is there.

Function and class header comments

  • Open with one short line, phrased so that someone scanning search results can tell whether it is relevant to them:
    • third person without a subject for anything that acts (Writes the value to the file.) or defines (Defines the supported protocols.)
    • a plain noun phrase for anything that only holds a value (The unique ID of the dashboard.)
  • Add more only where a reader needs it in order not to break the code, and cannot get it from the code itself or its immediate surroundings (examples below). Having nothing to add is the normal case.
    • an ordering constraint
    • an invariant
    • coupling to something in another file
    • a protocol detail
    • an approach that was tried and does not work
    • why something a reader would expect to find is deliberately absent
  • Prefer explaining why over what. The test is whether a reader could recover the sentence at a glance from the body itself; if they could, cut it.
  • When adding or changing a public or exported function, variable or constant, give it a comment that at least states what it does.