{"id":51429475,"url":"https://github.com/bnomei/emdash-fields","last_synced_at":"2026-07-05T03:02:04.094Z","repository":{"id":365487098,"uuid":"1267747085","full_name":"bnomei/emdash-fields","owner":"bnomei","description":"Structured JSON fields for EmDash, including object, structure, link, and choices editors.","archived":false,"fork":false,"pushed_at":"2026-06-27T15:13:16.000Z","size":439,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-27T17:10:31.095Z","etag":null,"topics":["astro","cms","emdash","emdash-plugin","field-widget","forms","json","react","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bnomei.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"bnomei","buy_me_a_coffee":"bnomei"}},"created_at":"2026-06-12T20:32:41.000Z","updated_at":"2026-06-18T20:30:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bnomei/emdash-fields","commit_stats":null,"previous_names":["bnomei/emdash-fields"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/bnomei/emdash-fields","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-fields","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-fields/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-fields/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-fields/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bnomei","download_url":"https://codeload.github.com/bnomei/emdash-fields/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-fields/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35141966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astro","cms","emdash","emdash-plugin","field-widget","forms","json","react","typescript"],"created_at":"2026-07-05T03:02:03.591Z","updated_at":"2026-07-05T03:02:04.086Z","avatar_url":"https://github.com/bnomei.png","language":"TypeScript","funding_links":["https://github.com/sponsors/bnomei","https://buymeacoffee.com/bnomei"],"categories":[],"sub_categories":[],"readme":"# @bnomei/emdash-fields\n\n[![npm version](https://img.shields.io/npm/v/@bnomei/emdash-fields.svg)](https://www.npmjs.com/package/@bnomei/emdash-fields)\n[![npm downloads](https://img.shields.io/npm/dm/@bnomei/emdash-fields.svg)](https://www.npmjs.com/package/@bnomei/emdash-fields)\n[![license](https://img.shields.io/npm/l/@bnomei/emdash-fields.svg)](https://www.npmjs.com/package/@bnomei/emdash-fields)\n[![types](https://img.shields.io/badge/types-included-blue.svg)](./package.json)\n[![source](https://img.shields.io/badge/source-GitHub-181717.svg?logo=github)](https://github.com/bnomei/emdash-fields)\n\nStructured JSON fields for EmDash.\n\n`@bnomei/emdash-fields` is a native EmDash plugin for JSON-backed\nfield controls that need a real editor but do not need a full block or layout\nbuilder. It registers reusable object, structure, link, and choices widgets,\nstores plain JSON values, and exports TypeScript option/value types for schema\ndefinitions and frontend renderers.\n\nUse it for compact content models such as CTAs, specs, settings, card metadata,\nbutton groups, and link objects. Editors get focused form controls; developers\nget predictable JSON without plugin-specific wrappers.\n\n## What It Provides\n\n- Native EmDash plugin factory: `fieldsPlugin()`.\n- JSON field widgets: `fields:object`, `fields:structure`, `fields:link`, and\n  `fields:choices`.\n- Plain JSON stored values without plugin-specific wrappers.\n- Admin UI built with [Kumo UI](https://kumo-ui.com/) with full light and dark\n  mode support.\n- TypeScript helper types such as `LinkValue`, `ObjectOptions`,\n  `StructureOptions`, and `ChoicesOptions`.\n\n## Why Use It\n\nEmDash already covers scalar fields, rich text, media, references, and raw JSON.\nFields adds the missing middle layer: structured JSON values with a purpose-built\nadmin UI.\n\n- Use `object` for one structured object, such as a CTA or settings group.\n- Use `structure` for repeatable structured rows, such as specs, links, stats, or\n  feature bullets.\n- Use `link` for a typed link value with text and target metadata.\n- Use `choices` when radio or checkbox-style choices are clearer than a\n  select box.\n- Use `choices` with `orientation: \"horizontal\"` and `columns` when choice\n  cards should sit side by side and wrap after a fixed count.\n\nFor larger page composition, pair it with `@bnomei/emdash-blocks` and\n`@bnomei/emdash-bento`.\n\n## Install\n\n```sh\nnpm install @bnomei/emdash-fields\n```\n\nRegister the plugin in `astro.config.mjs`:\n\n```js\nimport emdash from \"emdash/astro\";\nimport { fieldsPlugin } from \"@bnomei/emdash-fields\";\n\nexport default {\n  integrations: [\n    emdash({\n      plugins: [fieldsPlugin()],\n    }),\n  ],\n};\n```\n\n## Widgets\n\nThe plugin currently provides these widgets for `json` fields:\n\n- `fields:object`: a configured object editor.\n- `fields:structure`: a repeatable structured data editor.\n- `fields:link`: a typed link editor.\n- `fields:choices`: radio or checkbox-style option groups.\n\n## Subfield Types\n\nFields widgets attach to a normal EmDash schema field whose top-level `type` is\n`json`. For `object` and `structure` widgets, the nested `options.fields[].type`\nvalue chooses the editor control for that JSON property.\n\nThose nested `type` values use familiar EmDash field-type names for simple\nscalar inputs. They only describe the subfield inside the JSON widget; they do\nnot create nested EmDash schema fields. If omitted, the subfield defaults to\n`text`.\n\nSupported subfield types:\n\n| Type       | Editor control                  | Stored value                      |\n| ---------- | ------------------------------- | --------------------------------- |\n| `text`     | Single-line text input          | String                            |\n| `textarea` | Multi-line text area            | String                            |\n| `number`   | Number input                    | Number, or `undefined` when empty |\n| `integer`  | Number input with integer steps | Number, or `undefined` when empty |\n| `boolean`  | Checkbox                        | Boolean                           |\n| `select`   | Select menu                     | Selected string value             |\n| `url`      | URL input                       | String                            |\n\nUse `select` with `options`, either as strings or `{ \"value\", \"label\" }`\nobjects:\n\n```json\n{\n  \"key\": \"tone\",\n  \"label\": \"Tone\",\n  \"type\": \"select\",\n  \"options\": [\"Calm\", \"Bold\", \"Technical\"]\n}\n```\n\nFor richer EmDash field types such as rich text, media, references, or repeaters,\nmodel them as normal EmDash fields or use a block/layout builder instead of\nnested Fields subfields.\n\n## Examples\n\nObject field:\n\n```json\n{\n  \"slug\": \"cta\",\n  \"label\": \"CTA\",\n  \"type\": \"json\",\n  \"widget\": \"fields:object\",\n  \"options\": {\n    \"fields\": [\n      { \"key\": \"headline\", \"label\": \"Headline\", \"type\": \"text\" },\n      { \"key\": \"text\", \"label\": \"Text\", \"type\": \"textarea\" }\n    ]\n  }\n}\n```\n\nLink field:\n\n```json\n{\n  \"slug\": \"primary_link\",\n  \"label\": \"Primary Link\",\n  \"type\": \"json\",\n  \"widget\": \"fields:link\"\n}\n```\n\nStructure field:\n\n```json\n{\n  \"slug\": \"specs\",\n  \"label\": \"Specs\",\n  \"type\": \"json\",\n  \"widget\": \"fields:structure\",\n  \"options\": {\n    \"itemLabel\": \"Spec\",\n    \"fields\": [\n      { \"key\": \"label\", \"label\": \"Label\", \"type\": \"text\" },\n      { \"key\": \"value\", \"label\": \"Value\", \"type\": \"text\" }\n    ]\n  }\n}\n```\n\nThe structure widget also accepts `min` and `max` row counts. These constrain\nthe interactive controls — the Add button is disabled at `max` and the Remove\nbutton is disabled at `min` — guiding editors back within bounds. They are not\nenforced on externally supplied values: a stored array outside the limits is\nshown as-is rather than silently trimmed (which would drop data) or padded with\nempty rows (which would dirty the entry on open).\n\nChoices with horizontal cards:\n\n```json\n{\n  \"slug\": \"demo_mode\",\n  \"label\": \"Demo mode\",\n  \"type\": \"json\",\n  \"widget\": \"fields:choices\",\n  \"options\": {\n    \"orientation\": \"horizontal\",\n    \"columns\": 3,\n    \"choices\": [\n      {\n        \"value\": \"workers-ai\",\n        \"label\": \"Workers AI\",\n        \"description\": \"Run inference from Workers.\",\n        \"icon\": \"AI\"\n      },\n      {\n        \"value\": \"vectorize\",\n        \"label\": \"Vectorize\",\n        \"description\": \"Store embeddings for retrieval.\",\n        \"icon\": \"V\"\n      },\n      {\n        \"value\": \"ai-gateway\",\n        \"label\": \"AI Gateway\",\n        \"description\": \"Observe and control model traffic.\",\n        \"icon\": \"AG\"\n      }\n    ]\n  }\n}\n```\n\n### Choice Icons\n\nChoice icons should be either schema-safe strings or code-defined React elements:\n\n- Short plain strings such as `\"AI\"`, `\"V\"`, or `\"✓\"` render as compact text\n  tokens. These are the safest choice for JSON, YAML, or other serialized schema\n  configuration.\n- String image sources render as decorative images when they start with `http:`,\n  `https:`, `/`, `./`, `../`, or `data:image/`. Prefer project-owned assets such\n  as `/icons/ai.svg` or `./icons/vectorize.svg` when the schema is serialized.\n- React icon elements are supported only when choices are defined in TypeScript or\n  JavaScript code, for example `icon: \u003cBrainIcon size={16} weight=\"duotone\" /\u003e`.\n  Do not place JSX, component names, functions, or object literals in serialized\n  schema files.\n\nSchema-safe icon examples:\n\n```json\n{ \"value\": \"workers-ai\", \"label\": \"Workers AI\", \"icon\": \"AI\" }\n{ \"value\": \"vectorize\", \"label\": \"Vectorize\", \"icon\": \"/icons/vectorize.svg\" }\n{ \"value\": \"gateway\", \"label\": \"AI Gateway\", \"icon\": \"./icons/gateway.svg\" }\n```\n\nAvoid ambiguous or unsafe serialized values such as `\"BrainIcon\"` when it is meant\nto reference a component, raw `\u003csvg\u003e...\u003c/svg\u003e` markup, JavaScript expressions, or\nnon-image `data:` URLs.\n\n## Stored Values\n\nAll widgets store plain JSON values. They do not add plugin-specific wrappers, so frontend templates can read the field value directly.\n\nThe package exports TypeScript helper types such as `LinkValue`, `ObjectOptions`, `StructureOptions`, `FieldsChoice`, and `ChoicesOptions`.\n\n## Package Surface\n\n- ESM entry: `@bnomei/emdash-fields`.\n- Admin entry: `@bnomei/emdash-fields/admin`, including the widget\n  components and pure value helpers used by those widgets.\n- Type declarations are included from `dist/`.\n- Peer dependencies: `emdash` `\u003e=0.17.0`, `react` `^18.0.0 || ^19.0.0`,\n  `react-dom` `^18.0.0 || ^19.0.0`, `@cloudflare/kumo` `^2.5.0`, and `@phosphor-icons/react`\n  `^2.1.10`.\n\n## Status\n\nThis package ships as a native EmDash plugin because the widgets are trusted React admin field widgets. Package exports point at `vp pack`-built `dist/` JavaScript and declarations.\n\n## Related Packages\n\n- `@bnomei/emdash-blocks` provides ordered block-list editing.\n- `@bnomei/emdash-bento` provides row and column layout editing\n  with nested blocks.\n\n## License\n\nMIT.\n\n## Screenshot\n\n![Fields screenshot](./screenshot.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnomei%2Femdash-fields","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbnomei%2Femdash-fields","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnomei%2Femdash-fields/lists"}