diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c279aee --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.yml text eol=lf +*.yaml text eol=lf +*.md text eol=lf +*.js text eol=lf diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..119aea9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,83 @@ +# AGENTS.md + +## Build/Lint/Test Commands + +### Running Tests +- `pnpm test` - Run all tests once +- `pnpm run test:watch` - Run tests in watch mode +- `pnpm run test:ui` - Run tests with Vitest UI +- `pnpm run test:coverage` - Run tests with coverage report +- `pnpm test -- --reporter=verbose` - Run single test with verbose output + +### Build Commands +- `pnpm run build` - No explicit build command found, project is a single JS file +- The main output file is `trmnl-form-builder.js` + +### Linting +No explicit linting commands found. Project appears to use standard JavaScript without linter configuration. + +## Code Style Guidelines + +### Imports +- Uses ES modules with `import` statements +- No specific import ordering requirements identified +- Component is registered as a custom element using `customElements.define()` + +### Formatting +- Uses standard JavaScript formatting conventions +- Indentation appears to be 2 spaces (based on code review) +- No specific formatter configured (prettier, eslint, etc.) + +### Types +- No explicit type definitions found +- Uses vanilla JavaScript without TypeScript +- Properties are set directly on elements + +### Naming Conventions +- Component name: `trmnl-form-builder` +- Method names follow camelCase convention +- Property names follow camelCase convention +- Constants are uppercase with underscores when appropriate + +### Error Handling +- Basic error handling through try/catch blocks where needed +- Validation of inputs and edge cases in functions like escapeYaml +- No comprehensive error logging or centralized error handling pattern + +### Code Structure +- Single JavaScript file implementation +- Uses class-based component structure for custom element +- Modular approach with separate methods for different functionality +- Self-contained component with no external dependencies beyond browser APIs + +### Testing +- Unit tests using Vitest framework +- Tests cover core functionality like YAML escaping/unescaping +- Tests use `describe`, `it`, and `expect` patterns +- Tests run in jsdom environment to simulate browser context + +### Field Types +The form builder supports various field types including: +- string, textarea, number, email, url, password, select, checkbox, radio, author_bio +- xhrSelect and xhrSelectSearch for dynamic dropdowns with dependencies +- Special fields like copyable and copyable_webhook_url + +### Depends On Functionality +Fields can depend on other xhrSelect fields using the `depends_on` property. When a field has a `depends_on` property: +- The referenced parent field must be an xhrSelect type +- The parent field must appear before the child field in the form +- If these conditions are not met, the depends_on reference is automatically cleared +- This functionality ensures that dynamic dropdowns only load options when their dependencies are satisfied + +### YAML Generation +The component generates YAML configuration files for form definitions with proper escaping of special characters and handling of complex data types including: +- String values with proper quoting +- Label:Value pairs in select options +- Special handling for boolean literals and other YAML reserved words +- Proper indentation and structure according to YAML standards + +### Additional Notes +- Component uses custom element API (`customElements.define`) +- Implements shadow DOM for encapsulation +- Follows web standards for component development +- No build tools or transpilation steps identified \ No newline at end of file diff --git a/trmnl-form-builder.js b/trmnl-form-builder.js index 91d3bc8..79e8e12 100644 --- a/trmnl-form-builder.js +++ b/trmnl-form-builder.js @@ -1833,15 +1833,16 @@ class TRMLYamlForm extends HTMLElement { } // Capture any i18n description keys - Object.keys(field).forEach(k => { - if (k.startsWith('description-')) { - lines.push( ` ${k}: "${field[k]}"`); - } + Object.keys(field) + .filter(k => k.startsWith('description-')) + .sort() + .forEach(k => { + lines.push(` ${k}: ${this.escapeYaml(field[k])}`); }); }); - return lines.join('\n'); + return lines.join('\n').replace(/\r\n/g, '\n').replace(/\r/g, '\n'); } escapeYaml(value) { @@ -2085,6 +2086,10 @@ class TRMLYamlForm extends HTMLElement { parseYaml(yamlText) { const fields = []; + yamlText = yamlText + .replace(/^\uFEFF/, '') // optional but recommended + .replace(/\r\n/g, '\n') + .replace(/\r/g, '\n') const lines = yamlText.split('\n'); let currentField = null; let currentArray = null;