@iyulab/u-widgets 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 iyulab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # u-widgets
2
+
3
+ Declarative, data-driven widget system for visualization and input.
4
+
5
+ Define your data. Map it to visual channels. The renderer does the rest.
6
+
7
+ ```json
8
+ {
9
+ "widget": "chart.bar",
10
+ "data": [
11
+ { "name": "A", "value": 30 },
12
+ { "name": "B", "value": 70 },
13
+ { "name": "C", "value": 45 }
14
+ ]
15
+ }
16
+ ```
17
+
18
+ That's a complete bar chart. Mapping is auto-inferred from data shape.
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @iyulab/u-widgets
24
+ ```
25
+
26
+ ```html
27
+ <script type="module">
28
+ import '@iyulab/u-widgets';
29
+ // For chart support (requires echarts peer dependency):
30
+ // import '@iyulab/u-widgets/charts';
31
+ </script>
32
+
33
+ <u-widget .spec=${{ widget: 'metric', data: { value: 42, unit: 'users' } }}></u-widget>
34
+ ```
35
+
36
+ ### CDN
37
+
38
+ ```html
39
+ <script src="https://cdn.jsdelivr.net/npm/@iyulab/u-widgets/dist/u-widgets.global.js"></script>
40
+ ```
41
+
42
+ ## Widget Types
43
+
44
+ | Widget | Purpose |
45
+ |---|---|
46
+ | `chart.bar`, `chart.line`, `chart.area`, `chart.pie`, `chart.scatter`, `chart.radar` | Data visualization |
47
+ | `metric`, `stat-group` | KPI numbers |
48
+ | `gauge`, `progress` | Value within range |
49
+ | `table`, `list`, `kv` | Structured data |
50
+ | `code`, `citation`, `status`, `steps`, `rating`, `video`, `gallery` | Content display |
51
+ | `form`, `confirm` | User input |
52
+ | `compose` | Widget composition |
53
+
54
+ ## Quick Examples
55
+
56
+ ```json
57
+ // Metric
58
+ { "widget": "metric", "data": { "value": 1284, "unit": "EA", "change": 12.5, "trend": "up" } }
59
+
60
+ // Table (columns auto-inferred)
61
+ { "widget": "table", "data": [{ "name": "Alice", "role": "Engineer" }, { "name": "Bob", "role": "Designer" }] }
62
+
63
+ // Form
64
+ { "widget": "form", "fields": [{ "field": "name", "type": "text", "required": true }], "actions": [{ "label": "Submit", "action": "submit" }] }
65
+
66
+ // Compose
67
+ { "widget": "compose", "layout": "grid", "children": [{ "widget": "metric", "data": { "value": 42 } }, { "widget": "gauge", "data": { "value": 73 } }] }
68
+ ```
69
+
70
+ ## Events
71
+
72
+ ```js
73
+ widget.addEventListener('u-widget-event', (e) => {
74
+ console.log(e.detail); // { type, widget, id?, action?, data? }
75
+ });
76
+ ```
77
+
78
+ ## MCP Server
79
+
80
+ AI assistants can use u-widgets via [MCP server](docs/mcp-server.md):
81
+
82
+ ```bash
83
+ npx @iyulab/u-widgets-mcp
84
+ ```
85
+
86
+ ## Documentation
87
+
88
+ - [Widget Reference](docs/widgets.md) — Schema, mapping, options, theming
89
+ - [MCP Server Guide](docs/mcp-server.md) — Setup for Claude Desktop, Claude Code, VS Code
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,94 @@
1
+ let b = null;
2
+ function _(a) {
3
+ b = a;
4
+ }
5
+ function $() {
6
+ return b ?? R;
7
+ }
8
+ const h = {
9
+ "": "text",
10
+ "@": "email",
11
+ "#": "number",
12
+ "?": "password",
13
+ "%": "tel",
14
+ "&": "url",
15
+ d: "date",
16
+ dt: "datetime",
17
+ t: "time",
18
+ s: "select",
19
+ r: "radio",
20
+ c: "checkbox",
21
+ "^": "toggle",
22
+ R: "range",
23
+ ms: "multiselect"
24
+ }, k = /^@(\w+)(\*)?(?:\{([^}]*)\})?(?:\(([^)]*)\))?\s*:\s*(.*)$/, N = /^@\[\s*(\w+)\s+"([^"]+)"(?:\s+(\w+))?\s*\]$/, y = /^([T@#?%&dts]|dt|c|r|ms|R|\^)?(\d*)?\[([^\]]*)\]$/;
25
+ function R(a, n) {
26
+ const l = [], r = [], c = a.split(`
27
+ `).map((o) => o.trim()).filter(Boolean);
28
+ for (const o of c) {
29
+ const e = N.exec(o);
30
+ if (e) {
31
+ const t = {
32
+ action: e[1],
33
+ label: e[2]
34
+ };
35
+ e[3] && (t.style = e[3]), t.action === "submit" && !t.style && (t.style = "primary"), t.action === "cancel" && !t.style && (t.style = "default"), r.push(t);
36
+ continue;
37
+ }
38
+ const i = k.exec(o);
39
+ if (!i) {
40
+ console.debug(`[u-widget:formdown] Skipping unrecognized line: "${o}"`);
41
+ continue;
42
+ }
43
+ const g = i[1], x = i[2] === "*", u = i[3] || void 0, p = i[4] || void 0, w = i[5].trim(), s = { field: g };
44
+ p && (s.label = p), x && (s.required = !0), u && (s.options = u.split(",").map((t) => t.trim()));
45
+ const m = y.exec(w);
46
+ if (m) {
47
+ const t = m[1] || "", d = m[2], f = m[3];
48
+ t === "T" ? (s.type = "textarea", d && (s.rows = parseInt(d, 10))) : s.type = h[t] ?? "text", f && E(f, s);
49
+ } else
50
+ s.type = "text";
51
+ l.push(s);
52
+ }
53
+ return { fields: l, actions: r };
54
+ }
55
+ function E(a, n) {
56
+ const l = a.split(",").map((r) => r.trim());
57
+ for (const r of l) {
58
+ const c = r.indexOf("=");
59
+ if (c === -1) continue;
60
+ const o = r.slice(0, c).trim(), e = r.slice(c + 1).trim();
61
+ switch (o) {
62
+ case "min":
63
+ n.min = isNaN(Number(e)) ? e : Number(e);
64
+ break;
65
+ case "max":
66
+ n.max = isNaN(Number(e)) ? e : Number(e);
67
+ break;
68
+ case "step":
69
+ n.step = Number(e);
70
+ break;
71
+ case "minlength":
72
+ n.minLength = Number(e);
73
+ break;
74
+ case "maxlength":
75
+ n.maxLength = Number(e);
76
+ break;
77
+ case "pattern":
78
+ n.pattern = e;
79
+ break;
80
+ case "placeholder":
81
+ n.placeholder = e;
82
+ break;
83
+ default:
84
+ console.debug(`[u-widget:formdown] Unknown inline attribute "${o}" on field "${n.field}"`);
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ export {
90
+ $ as g,
91
+ R as p,
92
+ _ as r
93
+ };
94
+ //# sourceMappingURL=formdown-BWJ6QGJs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formdown-BWJ6QGJs.js","sources":["../src/core/formdown.ts"],"sourcesContent":["import type { UWidgetFieldDefinition, UWidgetAction, FieldType } from './types.js';\n\n/**\n * Parse a formdown string into fields and actions.\n *\n * Formdown syntax (subset):\n * @fieldName*(Label){opt1,opt2}: type[]\n * @[action \"Label\"]\n *\n * Type markers:\n * [] text @[] email #[] number\n * ?[] password %[] tel &[] url\n * T{n}[] textarea d[] date dt[] datetime\n * t[] time s[] select r[] radio\n * c[] checkbox ^[] toggle R[] range\n * ms[] multiselect\n *\n * * = required, (Label) = display label, {a,b,c} = options\n */\nexport interface FormdownResult {\n fields: UWidgetFieldDefinition[];\n actions: UWidgetAction[];\n}\n\n// ── Parser Registry ──\n\nexport type FormdownParser = (input: string, data?: Record<string, unknown>) => FormdownResult;\n\nlet _externalParser: FormdownParser | null = null;\n\nexport function registerFormdownParser(parser: FormdownParser): void {\n _externalParser = parser;\n}\n\nexport function getFormdownParser(): FormdownParser {\n return _externalParser ?? parseFormdown;\n}\n\n// Type marker → field type\nconst TYPE_MAP: Record<string, string> = {\n '': 'text',\n '@': 'email',\n '#': 'number',\n '?': 'password',\n '%': 'tel',\n '&': 'url',\n 'd': 'date',\n 'dt': 'datetime',\n 't': 'time',\n 's': 'select',\n 'r': 'radio',\n 'c': 'checkbox',\n '^': 'toggle',\n 'R': 'range',\n 'ms': 'multiselect',\n};\n\n// Field line: @fieldName*{opt1,opt2}(Label): marker[]\n// Order: field, required(*), options({...}), label((...)), : type\nconst FIELD_RE =\n /^@(\\w+)(\\*)?(?:\\{([^}]*)\\})?(?:\\(([^)]*)\\))?\\s*:\\s*(.*)$/;\n\n// Action line: @[action \"Label\"] or @[action \"Label\" style]\nconst ACTION_RE =\n /^@\\[\\s*(\\w+)\\s+\"([^\"]+)\"(?:\\s+(\\w+))?\\s*\\]$/;\n\n// Type marker: prefix + [] with optional content\nconst MARKER_RE =\n /^([T@#?%&dts]|dt|c|r|ms|R|\\^)?(\\d*)?\\[([^\\]]*)\\]$/;\n\nexport function parseFormdown(input: string, _data?: Record<string, unknown>): FormdownResult {\n const fields: UWidgetFieldDefinition[] = [];\n const actions: UWidgetAction[] = [];\n\n const lines = input.split('\\n').map((l) => l.trim()).filter(Boolean);\n\n for (const line of lines) {\n // Try action line\n const actionMatch = ACTION_RE.exec(line);\n if (actionMatch) {\n const action: UWidgetAction = {\n action: actionMatch[1],\n label: actionMatch[2],\n };\n if (actionMatch[3]) {\n action.style = actionMatch[3] as UWidgetAction['style'];\n }\n // Auto-assign style for common actions\n if (action.action === 'submit' && !action.style) action.style = 'primary';\n if (action.action === 'cancel' && !action.style) action.style = 'default';\n actions.push(action);\n continue;\n }\n\n // Try field line\n const fieldMatch = FIELD_RE.exec(line);\n if (!fieldMatch) {\n console.debug(`[u-widget:formdown] Skipping unrecognized line: \"${line}\"`);\n continue;\n }\n\n const fieldName = fieldMatch[1];\n const required = fieldMatch[2] === '*';\n const optionsStr = fieldMatch[3] || undefined;\n const label = fieldMatch[4] || undefined;\n const typeStr = fieldMatch[5].trim();\n\n const field: UWidgetFieldDefinition = { field: fieldName };\n if (label) field.label = label;\n if (required) field.required = true;\n if (optionsStr) {\n field.options = optionsStr.split(',').map((o) => o.trim());\n }\n\n // Parse type marker\n const markerMatch = MARKER_RE.exec(typeStr);\n if (markerMatch) {\n const prefix = markerMatch[1] || '';\n const num = markerMatch[2];\n const attrs = markerMatch[3];\n\n // Textarea: T{n}[]\n if (prefix === 'T') {\n field.type = 'textarea';\n if (num) field.rows = parseInt(num, 10);\n } else {\n field.type = (TYPE_MAP[prefix] ?? 'text') as FieldType;\n }\n\n // Parse inline attributes: min=X, max=Y\n if (attrs) {\n parseInlineAttrs(attrs, field);\n }\n } else {\n field.type = 'text';\n }\n\n fields.push(field);\n }\n\n return { fields, actions };\n}\n\nfunction parseInlineAttrs(attrs: string, field: UWidgetFieldDefinition): void {\n const parts = attrs.split(',').map((p) => p.trim());\n for (const part of parts) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const key = part.slice(0, eq).trim();\n const val = part.slice(eq + 1).trim();\n switch (key) {\n case 'min':\n field.min = isNaN(Number(val)) ? val : Number(val);\n break;\n case 'max':\n field.max = isNaN(Number(val)) ? val : Number(val);\n break;\n case 'step':\n field.step = Number(val);\n break;\n case 'minlength':\n field.minLength = Number(val);\n break;\n case 'maxlength':\n field.maxLength = Number(val);\n break;\n case 'pattern':\n field.pattern = val;\n break;\n case 'placeholder':\n field.placeholder = val;\n break;\n default:\n console.debug(`[u-widget:formdown] Unknown inline attribute \"${key}\" on field \"${field.field}\"`);\n break;\n }\n }\n}\n"],"names":["_externalParser","registerFormdownParser","parser","getFormdownParser","parseFormdown","TYPE_MAP","FIELD_RE","ACTION_RE","MARKER_RE","input","_data","fields","actions","lines","l","line","actionMatch","action","fieldMatch","fieldName","required","optionsStr","label","typeStr","field","o","markerMatch","prefix","num","attrs","parseInlineAttrs","parts","p","part","eq","key","val"],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ function b(t) {
2
+ return !t || t.length < 6 || t.length > 30 ? !1 : !!(/^\d{4}-\d{2}-\d{2}/.test(t) || /^\d{1,2}\/\d{1,2}\/\d{4}$/.test(t) || /^\d{4}\/\d{2}\/\d{2}$/.test(t) || /^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i.test(t));
3
+ }
4
+ function S(t, n) {
5
+ if (n == null) return;
6
+ const o = Array.isArray(n) ? n : [n];
7
+ if (o.length === 0) return;
8
+ const f = o[0], a = Object.keys(f);
9
+ if (a.length !== 0) {
10
+ if (t.startsWith("chart."))
11
+ return m(t, f, a);
12
+ if (t === "table")
13
+ return y(a);
14
+ if (t === "list")
15
+ return A(f, a);
16
+ }
17
+ }
18
+ function h(t, n) {
19
+ typeof console < "u" && console.debug && console.debug(`[u-widget:infer] ${t}: ${n}`);
20
+ }
21
+ function m(t, n, o) {
22
+ if (t === "chart.pie" || t === "chart.funnel") {
23
+ const e = o.find((s) => typeof n[s] == "string"), i = o.find((s) => typeof n[s] == "number");
24
+ if (e && i)
25
+ return { label: e, value: i };
26
+ h(t, "needs 1 string + 1 number field");
27
+ return;
28
+ }
29
+ if (t === "chart.heatmap") {
30
+ const e = o.filter((s) => typeof n[s] == "string"), i = o.find((s) => typeof n[s] == "number");
31
+ if (e.length >= 2 && i)
32
+ return { x: e[0], y: e[1], value: i };
33
+ h(t, "needs 2 string + 1 number field");
34
+ return;
35
+ }
36
+ if (t === "chart.radar") {
37
+ const e = o.find((s) => typeof n[s] == "string"), i = o.filter((s) => typeof n[s] == "number");
38
+ if (e && i.length > 0)
39
+ return { axis: e, y: i.length === 1 ? i[0] : i };
40
+ h(t, "needs 1 string (axis) + 1+ number field");
41
+ return;
42
+ }
43
+ if (t === "chart.box") {
44
+ const e = o.filter((c) => typeof n[c] == "number");
45
+ if (e.length < 5) return;
46
+ const i = o.find((c) => typeof n[c] == "string"), r = ["min", "q1", "median", "q3", "max"].filter((c) => e.includes(c)), l = r.length === 5 ? r : e.slice(0, 5);
47
+ return { x: i, y: l };
48
+ }
49
+ const f = o.filter((e) => typeof n[e] == "string"), d = f.find((e) => b(String(n[e]))) ?? f[0], u = o.filter((e) => typeof n[e] == "number");
50
+ if (d && u.length > 0)
51
+ return { x: d, y: u.length === 1 ? u[0] : u };
52
+ if (t === "chart.scatter" && u.length >= 2) {
53
+ const [e, ...i] = u;
54
+ return { x: e, y: i.length === 1 ? i[0] : i };
55
+ }
56
+ h(t, `no suitable fields found (strings: ${o.filter((e) => typeof n[e] == "string").length}, numbers: ${u.length})`);
57
+ }
58
+ function y(t) {
59
+ return {
60
+ columns: t.map((n) => ({ field: n }))
61
+ };
62
+ }
63
+ const g = /* @__PURE__ */ new Set(["avatar", "image", "photo", "picture", "thumbnail", "img", "profileimage", "profileImage"]), p = /* @__PURE__ */ new Set(["icon", "emoji", "symbol"]), v = /* @__PURE__ */ new Set(["badge", "tag", "category"]), F = /* @__PURE__ */ new Set(["trailing", "amount", "count", "price", "status", "date", "time"]);
64
+ function x(t) {
65
+ return typeof t != "string" ? !1 : /^https?:\/\/.+/i.test(t);
66
+ }
67
+ function A(t, n) {
68
+ const o = n.filter((r) => typeof t[r] == "string");
69
+ if (o.length === 0) return;
70
+ const f = {
71
+ primary: o[0],
72
+ secondary: o.length > 1 ? o[1] : void 0
73
+ }, a = new Set([f.primary, f.secondary].filter(Boolean)), d = n.find(
74
+ (r) => !a.has(r) && (g.has(r.toLowerCase()) || typeof t[r] == "string" && g.has(r.toLowerCase()))
75
+ ), u = d ? void 0 : n.find(
76
+ (r) => !a.has(r) && g.has(r.toLowerCase()) && x(t[r])
77
+ ), e = d ?? u;
78
+ if (e && (f.avatar = e, a.add(e)), !e) {
79
+ const r = n.find((l) => !a.has(l) && p.has(l.toLowerCase()));
80
+ r && (f.icon = r, a.add(r));
81
+ }
82
+ const i = n.find((r) => !a.has(r) && F.has(r.toLowerCase()));
83
+ i && (f.trailing = i, a.add(i));
84
+ const s = n.find((r) => !a.has(r) && v.has(r.toLowerCase()));
85
+ return s && (f.badge = s), f;
86
+ }
87
+ export {
88
+ b as a,
89
+ S as i
90
+ };
91
+ //# sourceMappingURL=infer-CNOiD2dS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer-CNOiD2dS.js","sources":["../src/core/utils.ts","../src/core/infer.ts"],"sourcesContent":["/**\n * Shared utility functions for u-widgets core modules.\n */\n\n/**\n * Detect if a string value looks like a date.\n * Matches common patterns: YYYY-MM-DD, ISO 8601, MM/DD/YYYY, YYYY/MM/DD, month names.\n *\n * @param value - The string to test.\n * @returns `true` if the string resembles a date format.\n */\nexport function isDateLikeString(value: string): boolean {\n if (!value || value.length < 6 || value.length > 30) return false;\n // ISO 8601 (2024-01-15, 2024-01-15T10:30:00Z)\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(value)) return true;\n // MM/DD/YYYY or DD/MM/YYYY\n if (/^\\d{1,2}\\/\\d{1,2}\\/\\d{4}$/.test(value)) return true;\n // YYYY/MM/DD\n if (/^\\d{4}\\/\\d{2}\\/\\d{2}$/.test(value)) return true;\n // Month names: \"Jan 2024\", \"January 15, 2024\"\n if (/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i.test(value)) return true;\n return false;\n}\n","import type { UWidgetMapping } from './types.js';\nimport { isDateLikeString } from './utils.js';\n\n/**\n * Auto-infer a mapping from data shape and widget type.\n *\n * Inference rules:\n * - **chart.bar/line/area:** first string field → `x`, number fields → `y`\n * - **chart.pie/funnel:** first string → `label`, first number → `value`\n * - **chart.scatter:** first two number fields → `x`, `y`\n * - **chart.radar:** first string → `axis`, number fields → `y`\n * - **chart.heatmap:** two string fields + number → `x`, `y`, `value`\n * - **chart.box:** string → `x`, five number fields (min/q1/median/q3/max) → `y`\n * - **table:** all keys → `columns`\n * - **list:** first string → `primary`, second string → `secondary`\n * - **metric/gauge/progress/form:** no mapping needed (returns `undefined`)\n *\n * @param widget - Widget type identifier.\n * @param data - The spec's `data` field (object or array of records).\n * @returns Inferred mapping, or `undefined` if not applicable.\n *\n * @example\n * ```ts\n * infer('chart.bar', [{ name: 'A', value: 30 }]);\n * // → { x: 'name', y: 'value' }\n * ```\n */\nexport function infer(\n widget: string,\n data: Record<string, unknown> | Record<string, unknown>[] | undefined,\n): UWidgetMapping | undefined {\n if (data == null) return undefined;\n\n const records = Array.isArray(data) ? data : [data];\n if (records.length === 0) return undefined;\n\n const sample = records[0];\n const keys = Object.keys(sample);\n if (keys.length === 0) return undefined;\n\n // chart.* → x (first string), y (first number)\n if (widget.startsWith('chart.')) {\n return inferChart(widget, sample, keys);\n }\n\n // table → all keys as columns\n if (widget === 'table') {\n return inferTable(keys);\n }\n\n // list → first string as primary, second as secondary\n if (widget === 'list') {\n return inferList(sample, keys);\n }\n\n // metric, gauge, progress, stat-group → no mapping needed\n // form, confirm → handled by fields, not mapping\n return undefined;\n}\n\n// Debug logging helper — only logs in development builds\nfunction debugInfer(widget: string, message: string): void {\n if (typeof console !== 'undefined' && console.debug) {\n console.debug(`[u-widget:infer] ${widget}: ${message}`);\n }\n}\n\nfunction inferChart(\n widget: string,\n sample: Record<string, unknown>,\n keys: string[],\n): UWidgetMapping | undefined {\n if (widget === 'chart.pie' || widget === 'chart.funnel') {\n const labelField = keys.find((k) => typeof sample[k] === 'string');\n const valueField = keys.find((k) => typeof sample[k] === 'number');\n if (labelField && valueField) {\n return { label: labelField, value: valueField };\n }\n debugInfer(widget, 'needs 1 string + 1 number field');\n return undefined;\n }\n\n if (widget === 'chart.heatmap') {\n const stringFields = keys.filter((k) => typeof sample[k] === 'string');\n const numberField = keys.find((k) => typeof sample[k] === 'number');\n if (stringFields.length >= 2 && numberField) {\n return { x: stringFields[0], y: stringFields[1], value: numberField };\n }\n debugInfer(widget, 'needs 2 string + 1 number field');\n return undefined;\n }\n\n if (widget === 'chart.radar') {\n const axisField = keys.find((k) => typeof sample[k] === 'string');\n const numFields = keys.filter((k) => typeof sample[k] === 'number');\n if (axisField && numFields.length > 0) {\n return { axis: axisField, y: numFields.length === 1 ? numFields[0] : numFields };\n }\n debugInfer(widget, 'needs 1 string (axis) + 1+ number field');\n return undefined;\n }\n\n if (widget === 'chart.box') {\n const numFields = keys.filter((k) => typeof sample[k] === 'number');\n if (numFields.length < 5) return undefined;\n const xField = keys.find((k) => typeof sample[k] === 'string');\n // Try well-known names first, then take first 5 number fields\n const wellKnown = ['min', 'q1', 'median', 'q3', 'max'];\n const matched = wellKnown.filter((name) => numFields.includes(name));\n const statFields = matched.length === 5 ? matched : numFields.slice(0, 5);\n return { x: xField, y: statFields };\n }\n\n // bar, line, area, scatter → x/y\n // Prefer date-like string fields as x-axis (common time-series pattern)\n const stringFields = keys.filter((k) => typeof sample[k] === 'string');\n const dateField = stringFields.find((k) => isDateLikeString(String(sample[k])));\n const xField = dateField ?? stringFields[0];\n const numFields = keys.filter((k) => typeof sample[k] === 'number');\n\n if (xField && numFields.length > 0) {\n return { x: xField, y: numFields.length === 1 ? numFields[0] : numFields };\n }\n\n // scatter: all-numeric data → first number = x, rest = y\n if (widget === 'chart.scatter' && numFields.length >= 2) {\n const [xNum, ...yNums] = numFields;\n return { x: xNum, y: yNums.length === 1 ? yNums[0] : yNums };\n }\n\n debugInfer(widget, `no suitable fields found (strings: ${keys.filter(k => typeof sample[k] === 'string').length}, numbers: ${numFields.length})`);\n return undefined;\n}\n\nfunction inferTable(keys: string[]): UWidgetMapping {\n return {\n columns: keys.map((k) => ({ field: k })),\n };\n}\n\n// Well-known field name sets for list auto-inference\nconst AVATAR_NAMES = new Set(['avatar', 'image', 'photo', 'picture', 'thumbnail', 'img', 'profileimage', 'profileImage']);\nconst ICON_NAMES = new Set(['icon', 'emoji', 'symbol']);\nconst BADGE_NAMES = new Set(['badge', 'tag', 'category']);\nconst TRAILING_NAMES = new Set(['trailing', 'amount', 'count', 'price', 'status', 'date', 'time']);\n\nfunction looksLikeUrl(value: unknown): boolean {\n if (typeof value !== 'string') return false;\n return /^https?:\\/\\/.+/i.test(value);\n}\n\nfunction inferList(\n sample: Record<string, unknown>,\n keys: string[],\n): UWidgetMapping | undefined {\n const stringKeys = keys.filter((k) => typeof sample[k] === 'string');\n if (stringKeys.length === 0) return undefined;\n\n const mapping: UWidgetMapping = {\n primary: stringKeys[0],\n secondary: stringKeys.length > 1 ? stringKeys[1] : undefined,\n };\n\n // Detect avatar field — well-known name OR URL-like string value\n const usedKeys = new Set([mapping.primary, mapping.secondary].filter(Boolean));\n const avatarKey = keys.find((k) =>\n !usedKeys.has(k) && (AVATAR_NAMES.has(k.toLowerCase()) || (typeof sample[k] === 'string' && AVATAR_NAMES.has(k.toLowerCase())))\n );\n // Also check URL pattern for avatar candidates\n const avatarByUrl = !avatarKey ? keys.find((k) =>\n !usedKeys.has(k) && AVATAR_NAMES.has(k.toLowerCase()) && looksLikeUrl(sample[k])\n ) : undefined;\n const resolvedAvatar = avatarKey ?? avatarByUrl;\n\n if (resolvedAvatar) {\n mapping.avatar = resolvedAvatar;\n usedKeys.add(resolvedAvatar);\n }\n\n // Detect icon field — well-known name, mutually exclusive with avatar\n if (!resolvedAvatar) {\n const iconKey = keys.find((k) => !usedKeys.has(k) && ICON_NAMES.has(k.toLowerCase()));\n if (iconKey) {\n mapping.icon = iconKey;\n usedKeys.add(iconKey);\n }\n }\n\n // Detect trailing field — well-known name, mutually exclusive with primary/secondary\n const trailingKey = keys.find((k) => !usedKeys.has(k) && TRAILING_NAMES.has(k.toLowerCase()));\n if (trailingKey) {\n mapping.trailing = trailingKey;\n usedKeys.add(trailingKey);\n }\n\n // Detect badge field — well-known name\n const badgeKey = keys.find((k) => !usedKeys.has(k) && BADGE_NAMES.has(k.toLowerCase()));\n if (badgeKey) {\n mapping.badge = badgeKey;\n }\n\n return mapping;\n}\n\n"],"names":["isDateLikeString","value","infer","widget","data","records","sample","keys","inferChart","inferTable","inferList","debugInfer","message","labelField","k","valueField","stringFields","numberField","axisField","numFields","xField","matched","name","statFields","xNum","yNums","AVATAR_NAMES","ICON_NAMES","BADGE_NAMES","TRAILING_NAMES","looksLikeUrl","stringKeys","mapping","usedKeys","avatarKey","avatarByUrl","resolvedAvatar","iconKey","trailingKey","badgeKey"],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import { g as r } from "./formdown-BWJ6QGJs.js";
2
+ import { css as g } from "lit";
3
+ function o(a) {
4
+ const e = { ...a };
5
+ if (e.mapping && "fields" in e.mapping && !e.fields) {
6
+ const { fields: t, ...i } = e.mapping;
7
+ e.fields = t, e.mapping = Object.keys(i).length > 0 ? i : void 0;
8
+ }
9
+ if (e.formdown && !e.fields)
10
+ try {
11
+ const i = r()(e.formdown, e.data);
12
+ e.fields = i.fields, i.actions.length > 0 && !e.actions && (e.actions = i.actions);
13
+ } catch (t) {
14
+ console.warn("[u-widget:normalize] Failed to parse formdown:", t.message);
15
+ }
16
+ return e;
17
+ }
18
+ function p(a) {
19
+ const { y: e, ...t } = a;
20
+ return {
21
+ ...t,
22
+ y: e == null ? void 0 : typeof e == "string" ? [e] : e
23
+ };
24
+ }
25
+ const n = g`
26
+ :host {
27
+ /* ── Spacing ── */
28
+ --u-widget-gap: 16px;
29
+ --u-widget-radius: 6px;
30
+
31
+ /* ── Typography ── */
32
+ --u-widget-font-family: system-ui, -apple-system, sans-serif;
33
+ --u-widget-font-size: 0.875rem;
34
+
35
+ /* ── Chart ── */
36
+ --u-widget-chart-height: 300px;
37
+
38
+ /* ── Shadow ── */
39
+ --u-widget-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);
40
+ --u-widget-shadow-hover: 0 4px 12px rgba(0,0,0,0.10), 0 2px 4px rgba(0,0,0,0.06);
41
+ }
42
+
43
+ @media (prefers-color-scheme: dark) {
44
+ :host(:not([theme="light"])) {
45
+ --u-widget-bg: #1e1e2e;
46
+ --u-widget-surface: #2a2a3e;
47
+ --u-widget-text: #e2e8f0;
48
+ --u-widget-text-secondary: #94a3b8;
49
+ --u-widget-border: #374151;
50
+ --u-widget-primary: #818cf8;
51
+ --u-widget-positive: #4ade80;
52
+ --u-widget-negative: #f87171;
53
+ --u-widget-warning: #fbbf24;
54
+ --u-widget-shadow: 0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2);
55
+ --u-widget-shadow-hover: 0 4px 12px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.2);
56
+ }
57
+ }
58
+
59
+ :host([theme="dark"]) {
60
+ --u-widget-bg: #1e1e2e;
61
+ --u-widget-surface: #2a2a3e;
62
+ --u-widget-text: #e2e8f0;
63
+ --u-widget-text-secondary: #94a3b8;
64
+ --u-widget-border: #374151;
65
+ --u-widget-primary: #818cf8;
66
+ --u-widget-positive: #4ade80;
67
+ --u-widget-negative: #f87171;
68
+ --u-widget-warning: #fbbf24;
69
+ --u-widget-shadow: 0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2);
70
+ --u-widget-shadow-hover: 0 4px 12px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.2);
71
+ }
72
+ `;
73
+ export {
74
+ p as a,
75
+ o as n,
76
+ n as t
77
+ };
78
+ //# sourceMappingURL=tokens-x1kDxgG8.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens-x1kDxgG8.js","sources":["../src/core/normalize.ts","../src/styles/tokens.ts"],"sourcesContent":["import type { UWidgetSpec, NormalizedMapping, UWidgetMapping } from './types.js';\nimport { getFormdownParser } from './formdown.js';\n\n/**\n * Normalize a spec for internal processing.\n *\n * Transformations applied:\n * 1. Deprecated `mapping.fields` → top-level `fields`\n * 2. `formdown` string → parsed `fields` + `actions`\n *\n * Returns a shallow copy; the original spec is not mutated.\n *\n * @param spec - The widget spec to normalize.\n * @returns A normalized copy of the spec.\n */\nexport function normalize(spec: UWidgetSpec): UWidgetSpec {\n const result = { ...spec };\n\n // Normalize deprecated mapping.fields → top-level fields\n if (result.mapping && 'fields' in result.mapping && !result.fields) {\n const { fields, ...restMapping } = result.mapping as UWidgetMapping & {\n fields?: UWidgetSpec['fields'];\n };\n result.fields = fields;\n result.mapping = Object.keys(restMapping).length > 0 ? restMapping : undefined;\n }\n\n // Normalize formdown → fields + actions\n if (result.formdown && !result.fields) {\n try {\n const parser = getFormdownParser();\n const parsed = parser(result.formdown, result.data as Record<string, unknown> | undefined);\n result.fields = parsed.fields;\n if (parsed.actions.length > 0 && !result.actions) {\n result.actions = parsed.actions;\n }\n } catch (e) {\n console.warn('[u-widget:normalize] Failed to parse formdown:', (e as Error).message);\n }\n }\n\n return result;\n}\n\n/**\n * Normalize `mapping.y` to always be `string[]`.\n *\n * Converts `y: \"field\"` → `y: [\"field\"]` and passes arrays through unchanged.\n *\n * @param mapping - The original mapping from a widget spec.\n * @returns A new mapping object with `y` guaranteed to be `string[]` or `undefined`.\n */\nexport function normalizeMapping(mapping: UWidgetMapping): NormalizedMapping {\n const { y, ...rest } = mapping;\n return {\n ...rest,\n y: y == null ? undefined : typeof y === 'string' ? [y] : y,\n };\n}\n","import { css } from 'lit';\n\n/**\n * Shared CSS custom property tokens for all u-widget elements.\n *\n * **Token categories:**\n * - Colors: bg, surface, text, text-secondary, border, primary, positive, negative, warning\n * - Spacing: gap, radius\n * - Typography: font-family, font-size\n * - Chart: chart-height, chart-color-1..10\n *\n * **Theme modes:**\n * 1. Light mode: No `:host` color defaults — each `var()` call has a light fallback.\n * This allows host applications to set `--u-widget-*` on any ancestor and have\n * the values inherited naturally through Shadow DOM.\n * 2. Dark mode via `prefers-color-scheme: dark` (automatic, unless `theme=\"light\"`)\n * 3. Dark mode via `theme=\"dark\"` attribute (manual override)\n *\n * Host applications can override any `--u-widget-*` variable at any ancestor.\n * Colors inherit automatically — no need to set `theme` attribute just for theming.\n * The `theme=\"dark\"` attribute is still useful for component-specific overrides\n * (e.g., code syntax highlight colors).\n */\nexport const themeStyles = css`\n :host {\n /* ── Spacing ── */\n --u-widget-gap: 16px;\n --u-widget-radius: 6px;\n\n /* ── Typography ── */\n --u-widget-font-family: system-ui, -apple-system, sans-serif;\n --u-widget-font-size: 0.875rem;\n\n /* ── Chart ── */\n --u-widget-chart-height: 300px;\n\n /* ── Shadow ── */\n --u-widget-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);\n --u-widget-shadow-hover: 0 4px 12px rgba(0,0,0,0.10), 0 2px 4px rgba(0,0,0,0.06);\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([theme=\"light\"])) {\n --u-widget-bg: #1e1e2e;\n --u-widget-surface: #2a2a3e;\n --u-widget-text: #e2e8f0;\n --u-widget-text-secondary: #94a3b8;\n --u-widget-border: #374151;\n --u-widget-primary: #818cf8;\n --u-widget-positive: #4ade80;\n --u-widget-negative: #f87171;\n --u-widget-warning: #fbbf24;\n --u-widget-shadow: 0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2);\n --u-widget-shadow-hover: 0 4px 12px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.2);\n }\n }\n\n :host([theme=\"dark\"]) {\n --u-widget-bg: #1e1e2e;\n --u-widget-surface: #2a2a3e;\n --u-widget-text: #e2e8f0;\n --u-widget-text-secondary: #94a3b8;\n --u-widget-border: #374151;\n --u-widget-primary: #818cf8;\n --u-widget-positive: #4ade80;\n --u-widget-negative: #f87171;\n --u-widget-warning: #fbbf24;\n --u-widget-shadow: 0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2);\n --u-widget-shadow-hover: 0 4px 12px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.2);\n }\n`;\n"],"names":["normalize","spec","result","fields","restMapping","parsed","getFormdownParser","e","normalizeMapping","mapping","y","rest","themeStyles","css"],"mappings":";;AAeO,SAASA,EAAUC,GAAgC;AACxD,QAAMC,IAAS,EAAE,GAAGD,EAAA;AAGpB,MAAIC,EAAO,WAAW,YAAYA,EAAO,WAAW,CAACA,EAAO,QAAQ;AAClE,UAAM,EAAE,QAAAC,GAAQ,GAAGC,EAAA,IAAgBF,EAAO;AAG1C,IAAAA,EAAO,SAASC,GAChBD,EAAO,UAAU,OAAO,KAAKE,CAAW,EAAE,SAAS,IAAIA,IAAc;AAAA,EACvE;AAGA,MAAIF,EAAO,YAAY,CAACA,EAAO;AAC7B,QAAI;AAEF,YAAMG,IADSC,EAAA,EACOJ,EAAO,UAAUA,EAAO,IAA2C;AACzF,MAAAA,EAAO,SAASG,EAAO,QACnBA,EAAO,QAAQ,SAAS,KAAK,CAACH,EAAO,YACvCA,EAAO,UAAUG,EAAO;AAAA,IAE5B,SAASE,GAAG;AACV,cAAQ,KAAK,kDAAmDA,EAAY,OAAO;AAAA,IACrF;AAGF,SAAOL;AACT;AAUO,SAASM,EAAiBC,GAA4C;AAC3E,QAAM,EAAE,GAAAC,GAAG,GAAGC,EAAA,IAASF;AACvB,SAAO;AAAA,IACL,GAAGE;AAAA,IACH,GAAGD,KAAK,OAAO,SAAY,OAAOA,KAAM,WAAW,CAACA,CAAC,IAAIA;AAAA,EAAA;AAE7D;ACnCO,MAAME,IAAcC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;"}