@f-ewald/components 0.5.0 → 0.7.0
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/README.md +61 -0
- package/custom-elements.json +3808 -930
- package/dist/chat-message.d.ts +39 -0
- package/dist/chat-message.d.ts.map +1 -0
- package/dist/chat-message.js +155 -0
- package/dist/chat-message.js.map +1 -0
- package/dist/data-table.d.ts +39 -0
- package/dist/data-table.d.ts.map +1 -0
- package/dist/data-table.js +119 -0
- package/dist/data-table.js.map +1 -0
- package/dist/dropdown-button.d.ts +37 -0
- package/dist/dropdown-button.d.ts.map +1 -0
- package/dist/dropdown-button.js +219 -0
- package/dist/dropdown-button.js.map +1 -0
- package/dist/editable-text.d.ts +52 -0
- package/dist/editable-text.d.ts.map +1 -0
- package/dist/editable-text.js +224 -0
- package/dist/editable-text.js.map +1 -0
- package/dist/form-select.d.ts +46 -0
- package/dist/form-select.d.ts.map +1 -0
- package/dist/form-select.js +245 -0
- package/dist/form-select.js.map +1 -0
- package/dist/frame-box.d.ts +22 -0
- package/dist/frame-box.d.ts.map +1 -0
- package/dist/frame-box.js +68 -0
- package/dist/frame-box.js.map +1 -0
- package/dist/gallery-item-variant.d.ts +21 -0
- package/dist/gallery-item-variant.d.ts.map +1 -0
- package/dist/gallery-item-variant.js +44 -0
- package/dist/gallery-item-variant.js.map +1 -0
- package/dist/gallery-item.d.ts +28 -0
- package/dist/gallery-item.d.ts.map +1 -0
- package/dist/gallery-item.js +54 -0
- package/dist/gallery-item.js.map +1 -0
- package/dist/icon-button.d.ts +27 -0
- package/dist/icon-button.d.ts.map +1 -0
- package/dist/icon-button.js +83 -0
- package/dist/icon-button.js.map +1 -0
- package/dist/icons.d.ts +8 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +8 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/live-timer.d.ts +31 -0
- package/dist/live-timer.d.ts.map +1 -0
- package/dist/live-timer.js +78 -0
- package/dist/live-timer.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +82 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/photo-gallery.d.ts +117 -0
- package/dist/photo-gallery.d.ts.map +1 -0
- package/dist/photo-gallery.js +620 -0
- package/dist/photo-gallery.js.map +1 -0
- package/dist/popover-panel.d.ts +37 -0
- package/dist/popover-panel.d.ts.map +1 -0
- package/dist/popover-panel.js +184 -0
- package/dist/popover-panel.js.map +1 -0
- package/dist/status-pill.d.ts +24 -0
- package/dist/status-pill.d.ts.map +1 -0
- package/dist/status-pill.js +101 -0
- package/dist/status-pill.js.map +1 -0
- package/dist/tile-grid.d.ts +32 -0
- package/dist/tile-grid.d.ts.map +1 -0
- package/dist/tile-grid.js +102 -0
- package/dist/tile-grid.js.map +1 -0
- package/dist/tokens.css +39 -0
- package/dist/tokens.d.ts +28 -3
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +42 -43
- package/dist/tokens.js.map +1 -1
- package/dist/user-avatar.d.ts +5 -2
- package/dist/user-avatar.d.ts.map +1 -1
- package/dist/user-avatar.js +22 -6
- package/dist/user-avatar.js.map +1 -1
- package/dist/utils/duration.d.ts +12 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +27 -0
- package/dist/utils/duration.js.map +1 -0
- package/docs/chat-message.md +59 -0
- package/docs/data-table.md +63 -0
- package/docs/dropdown-button.md +61 -0
- package/docs/editable-text.md +51 -0
- package/docs/form-select.md +70 -0
- package/docs/frame-box.md +43 -0
- package/docs/gallery-item-variant.md +37 -0
- package/docs/gallery-item.md +47 -0
- package/docs/icon-button.md +52 -0
- package/docs/live-timer.md +39 -0
- package/docs/mcp-evaluation.md +18 -11
- package/docs/photo-gallery.md +77 -0
- package/docs/popover-panel.md +70 -0
- package/docs/status-pill.md +43 -0
- package/docs/tile-grid.md +58 -0
- package/docs/user-avatar.md +1 -1
- package/llms.txt +357 -1
- package/package.json +10 -3
package/dist/tokens.css
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
:root {
|
|
2
|
+
color-scheme: light;
|
|
2
3
|
--ui-primary: #4f46e5;
|
|
3
4
|
--ui-primary-hover: #4338ca;
|
|
4
5
|
--ui-danger: #dc2626;
|
|
@@ -19,3 +20,41 @@
|
|
|
19
20
|
--ui-font-size: 0.875rem;
|
|
20
21
|
--ui-font-size-sm: 0.75rem;
|
|
21
22
|
}
|
|
23
|
+
|
|
24
|
+
@media (prefers-color-scheme: dark) {
|
|
25
|
+
:root:not([data-theme="light"]) {
|
|
26
|
+
color-scheme: dark;
|
|
27
|
+
--ui-primary: #6366f1;
|
|
28
|
+
--ui-primary-hover: #818cf8;
|
|
29
|
+
--ui-danger: #ef4444;
|
|
30
|
+
--ui-danger-hover: #f87171;
|
|
31
|
+
--ui-success: #22c55e;
|
|
32
|
+
--ui-text: #f1f5f9;
|
|
33
|
+
--ui-text-muted: #94a3b8;
|
|
34
|
+
--ui-border: #334155;
|
|
35
|
+
--ui-surface: #0f172a;
|
|
36
|
+
--ui-surface-muted: #1e293b;
|
|
37
|
+
--ui-overlay: rgb(2 6 23 / 0.6);
|
|
38
|
+
--ui-focus-ring: 0 0 0 3px rgb(99 102 241 / 0.45);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:root[data-theme="dark"] {
|
|
43
|
+
color-scheme: dark;
|
|
44
|
+
--ui-primary: #6366f1;
|
|
45
|
+
--ui-primary-hover: #818cf8;
|
|
46
|
+
--ui-danger: #ef4444;
|
|
47
|
+
--ui-danger-hover: #f87171;
|
|
48
|
+
--ui-success: #22c55e;
|
|
49
|
+
--ui-text: #f1f5f9;
|
|
50
|
+
--ui-text-muted: #94a3b8;
|
|
51
|
+
--ui-border: #334155;
|
|
52
|
+
--ui-surface: #0f172a;
|
|
53
|
+
--ui-surface-muted: #1e293b;
|
|
54
|
+
--ui-overlay: rgb(2 6 23 / 0.6);
|
|
55
|
+
--ui-focus-ring: 0 0 0 3px rgb(99 102 241 / 0.45);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
:root[data-theme="light"] {
|
|
59
|
+
color-scheme: light;
|
|
60
|
+
}
|
package/dist/tokens.d.ts
CHANGED
|
@@ -8,9 +8,34 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const tokenValues: Record<string, string>;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Dark-mode overrides for the same `--ui-*` custom properties, applied by
|
|
12
|
+
* `tokens.css` via `@media (prefers-color-scheme: dark)` and `[data-theme]`
|
|
13
|
+
* (see generate-tokens-css.mjs). Same Tailwind slate scale as the light
|
|
14
|
+
* palette, inverted, with primary/danger/success lightened one step (500
|
|
15
|
+
* instead of 600) for sufficient contrast against dark surfaces. Shape/type
|
|
16
|
+
* tokens are theme-independent and not repeated here.
|
|
17
|
+
*/
|
|
18
|
+
export declare const darkTokenValues: Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Historically a shared `:host { --ui-x: var(--ui-x, fallback); } ` block,
|
|
21
|
+
* re-declaring every token on each component's own host element as a
|
|
22
|
+
* "materialize the inherited value, or this fallback" trick. That pattern
|
|
23
|
+
* turned out to be a genuine bug, not a working default: browsers treat
|
|
24
|
+
* `--ui-x: var(--ui-x, ...)` on `:host` as a self-referencing declaration
|
|
25
|
+
* that computes to the guaranteed-invalid value REGARDLESS of what an
|
|
26
|
+
* ancestor (e.g. `:root`) set — the host's own `:host` rule wins the cascade
|
|
27
|
+
* over the ancestor's, so this dropped whatever the app actually set and
|
|
28
|
+
* silently substituted the component's own fallback everywhere. It went
|
|
29
|
+
* unnoticed because every component's fallback happened to equal the
|
|
30
|
+
* intended light-mode value — dark mode (a real, different value at
|
|
31
|
+
* `:root`) exposed it immediately (colors rendering as if no theme were
|
|
32
|
+
* applied at all). Fix: don't redeclare tokens on `:host` — plain
|
|
33
|
+
* inheritance already carries `:root`'s values into every shadow root
|
|
34
|
+
* correctly, and every component already has its own `var(--ui-x, fallback)`
|
|
35
|
+
* at each point of use, so nothing here is needed for the "zero external
|
|
36
|
+
* CSS" default to keep working. Kept as an empty export so the existing
|
|
37
|
+
* `static override styles = [tokens, css\`...\`]` import in every component
|
|
38
|
+
* keeps compiling.
|
|
14
39
|
*/
|
|
15
40
|
export declare const tokens: import("lit").CSSResult;
|
|
16
41
|
//# sourceMappingURL=tokens.d.ts.map
|
package/dist/tokens.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA4B9C,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA4B9C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAalD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,MAAM,yBAAQ,CAAC"}
|
package/dist/tokens.js
CHANGED
|
@@ -32,48 +32,47 @@ export const tokenValues = {
|
|
|
32
32
|
"--ui-font-size-sm": "0.75rem",
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Dark-mode overrides for the same `--ui-*` custom properties, applied by
|
|
36
|
+
* `tokens.css` via `@media (prefers-color-scheme: dark)` and `[data-theme]`
|
|
37
|
+
* (see generate-tokens-css.mjs). Same Tailwind slate scale as the light
|
|
38
|
+
* palette, inverted, with primary/danger/success lightened one step (500
|
|
39
|
+
* instead of 600) for sufficient contrast against dark surfaces. Shape/type
|
|
40
|
+
* tokens are theme-independent and not repeated here.
|
|
38
41
|
*/
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
--ui-primary:
|
|
42
|
-
--ui-
|
|
43
|
-
--ui-danger:
|
|
44
|
-
--ui-
|
|
45
|
-
--ui-
|
|
46
|
-
--ui-text:
|
|
47
|
-
--ui-
|
|
48
|
-
--ui-
|
|
49
|
-
--ui-surface:
|
|
50
|
-
--ui-
|
|
51
|
-
--ui-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
--ui-font-size: var(--ui-font-size, 0.875rem);
|
|
76
|
-
--ui-font-size-sm: var(--ui-font-size-sm, 0.75rem);
|
|
77
|
-
}
|
|
78
|
-
`;
|
|
42
|
+
export const darkTokenValues = {
|
|
43
|
+
"--ui-primary": "#6366f1", // indigo-500
|
|
44
|
+
"--ui-primary-hover": "#818cf8", // indigo-400
|
|
45
|
+
"--ui-danger": "#ef4444", // red-500
|
|
46
|
+
"--ui-danger-hover": "#f87171", // red-400
|
|
47
|
+
"--ui-success": "#22c55e", // green-500
|
|
48
|
+
"--ui-text": "#f1f5f9", // slate-100
|
|
49
|
+
"--ui-text-muted": "#94a3b8", // slate-400
|
|
50
|
+
"--ui-border": "#334155", // slate-700
|
|
51
|
+
"--ui-surface": "#0f172a", // slate-900
|
|
52
|
+
"--ui-surface-muted": "#1e293b", // slate-800
|
|
53
|
+
"--ui-overlay": "rgb(2 6 23 / 0.6)", // slate-950 / 60%
|
|
54
|
+
"--ui-focus-ring": "0 0 0 3px rgb(99 102 241 / 0.45)",
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Historically a shared `:host { --ui-x: var(--ui-x, fallback); } ` block,
|
|
58
|
+
* re-declaring every token on each component's own host element as a
|
|
59
|
+
* "materialize the inherited value, or this fallback" trick. That pattern
|
|
60
|
+
* turned out to be a genuine bug, not a working default: browsers treat
|
|
61
|
+
* `--ui-x: var(--ui-x, ...)` on `:host` as a self-referencing declaration
|
|
62
|
+
* that computes to the guaranteed-invalid value REGARDLESS of what an
|
|
63
|
+
* ancestor (e.g. `:root`) set — the host's own `:host` rule wins the cascade
|
|
64
|
+
* over the ancestor's, so this dropped whatever the app actually set and
|
|
65
|
+
* silently substituted the component's own fallback everywhere. It went
|
|
66
|
+
* unnoticed because every component's fallback happened to equal the
|
|
67
|
+
* intended light-mode value — dark mode (a real, different value at
|
|
68
|
+
* `:root`) exposed it immediately (colors rendering as if no theme were
|
|
69
|
+
* applied at all). Fix: don't redeclare tokens on `:host` — plain
|
|
70
|
+
* inheritance already carries `:root`'s values into every shadow root
|
|
71
|
+
* correctly, and every component already has its own `var(--ui-x, fallback)`
|
|
72
|
+
* at each point of use, so nothing here is needed for the "zero external
|
|
73
|
+
* CSS" default to keep working. Kept as an empty export so the existing
|
|
74
|
+
* `static override styles = [tokens, css\`...\`]` import in every component
|
|
75
|
+
* keeps compiling.
|
|
76
|
+
*/
|
|
77
|
+
export const tokens = css ``;
|
|
79
78
|
//# sourceMappingURL=tokens.js.map
|
package/dist/tokens.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,SAAS;IACT,cAAc,EAAE,SAAS,EAAE,aAAa;IACxC,oBAAoB,EAAE,SAAS,EAAE,aAAa;IAC9C,aAAa,EAAE,SAAS,EAAE,UAAU;IACpC,mBAAmB,EAAE,SAAS,EAAE,UAAU;IAC1C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,WAAW,EAAE,SAAS,EAAE,YAAY;IACpC,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAC1C,aAAa,EAAE,SAAS,EAAE,YAAY;IACtC,cAAc,EAAE,SAAS,EAAE,QAAQ;IACnC,oBAAoB,EAAE,SAAS,EAAE,WAAW;IAC5C,cAAc,EAAE,sBAAsB,EAAE,kBAAkB;IAE1D,gBAAgB;IAChB,aAAa,EAAE,QAAQ,EAAE,aAAa;IACtC,gBAAgB,EAAE,SAAS,EAAE,UAAU;IACvC,aAAa,EACX,kEAAkE,EAAE,YAAY;IAClF,gBAAgB,EACd,qEAAqE,EAAE,YAAY;IACrF,iBAAiB,EAAE,iCAAiC;IAEpD,OAAO;IACP,WAAW,EACT,oHAAoH;IACtH,gBAAgB,EAAE,UAAU;IAC5B,mBAAmB,EAAE,SAAS;CAC/B,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,SAAS;IACT,cAAc,EAAE,SAAS,EAAE,aAAa;IACxC,oBAAoB,EAAE,SAAS,EAAE,aAAa;IAC9C,aAAa,EAAE,SAAS,EAAE,UAAU;IACpC,mBAAmB,EAAE,SAAS,EAAE,UAAU;IAC1C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,WAAW,EAAE,SAAS,EAAE,YAAY;IACpC,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAC1C,aAAa,EAAE,SAAS,EAAE,YAAY;IACtC,cAAc,EAAE,SAAS,EAAE,QAAQ;IACnC,oBAAoB,EAAE,SAAS,EAAE,WAAW;IAC5C,cAAc,EAAE,sBAAsB,EAAE,kBAAkB;IAE1D,gBAAgB;IAChB,aAAa,EAAE,QAAQ,EAAE,aAAa;IACtC,gBAAgB,EAAE,SAAS,EAAE,UAAU;IACvC,aAAa,EACX,kEAAkE,EAAE,YAAY;IAClF,gBAAgB,EACd,qEAAqE,EAAE,YAAY;IACrF,iBAAiB,EAAE,iCAAiC;IAEpD,OAAO;IACP,WAAW,EACT,oHAAoH;IACtH,gBAAgB,EAAE,UAAU;IAC5B,mBAAmB,EAAE,SAAS;CAC/B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,cAAc,EAAE,SAAS,EAAE,aAAa;IACxC,oBAAoB,EAAE,SAAS,EAAE,aAAa;IAC9C,aAAa,EAAE,SAAS,EAAE,UAAU;IACpC,mBAAmB,EAAE,SAAS,EAAE,UAAU;IAC1C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,WAAW,EAAE,SAAS,EAAE,YAAY;IACpC,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAC1C,aAAa,EAAE,SAAS,EAAE,YAAY;IACtC,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,SAAS,EAAE,YAAY;IAC7C,cAAc,EAAE,mBAAmB,EAAE,kBAAkB;IACvD,iBAAiB,EAAE,kCAAkC;CACtD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA,EAAE,CAAC","sourcesContent":["import { css } from \"lit\";\n\n/**\n * Design token values, sourced from Tailwind v4's default theme palette.\n * Each entry maps a `--ui-*` custom property name to its default value.\n * These are the same values baked in as `var(--ui-*, <fallback>)` fallbacks\n * throughout component styles, so components render correctly with zero\n * external CSS. Consumers may override any of these by setting the custom\n * property on `:root` or an ancestor element (see `tokens.css`).\n */\nexport const tokenValues: Record<string, string> = {\n // Colors\n \"--ui-primary\": \"#4f46e5\", // indigo-600\n \"--ui-primary-hover\": \"#4338ca\", // indigo-700\n \"--ui-danger\": \"#dc2626\", // red-600\n \"--ui-danger-hover\": \"#b91c1c\", // red-700\n \"--ui-success\": \"#16a34a\", // green-600\n \"--ui-text\": \"#0f172a\", // slate-900\n \"--ui-text-muted\": \"#64748b\", // slate-500\n \"--ui-border\": \"#e2e8f0\", // slate-200\n \"--ui-surface\": \"#ffffff\", // white\n \"--ui-surface-muted\": \"#f8fafc\", // slate-50\n \"--ui-overlay\": \"rgb(15 23 42 / 0.45)\", // slate-900 / 45%\n\n // Shape / depth\n \"--ui-radius\": \"0.5rem\", // rounded-lg\n \"--ui-radius-sm\": \"0.25rem\", // rounded\n \"--ui-shadow\":\n \"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)\", // shadow-md\n \"--ui-shadow-lg\":\n \"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)\", // shadow-xl\n \"--ui-focus-ring\": \"0 0 0 3px rgb(79 70 229 / 0.35)\",\n\n // Type\n \"--ui-font\":\n 'ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"',\n \"--ui-font-size\": \"0.875rem\",\n \"--ui-font-size-sm\": \"0.75rem\",\n};\n\n/**\n * Dark-mode overrides for the same `--ui-*` custom properties, applied by\n * `tokens.css` via `@media (prefers-color-scheme: dark)` and `[data-theme]`\n * (see generate-tokens-css.mjs). Same Tailwind slate scale as the light\n * palette, inverted, with primary/danger/success lightened one step (500\n * instead of 600) for sufficient contrast against dark surfaces. Shape/type\n * tokens are theme-independent and not repeated here.\n */\nexport const darkTokenValues: Record<string, string> = {\n \"--ui-primary\": \"#6366f1\", // indigo-500\n \"--ui-primary-hover\": \"#818cf8\", // indigo-400\n \"--ui-danger\": \"#ef4444\", // red-500\n \"--ui-danger-hover\": \"#f87171\", // red-400\n \"--ui-success\": \"#22c55e\", // green-500\n \"--ui-text\": \"#f1f5f9\", // slate-100\n \"--ui-text-muted\": \"#94a3b8\", // slate-400\n \"--ui-border\": \"#334155\", // slate-700\n \"--ui-surface\": \"#0f172a\", // slate-900\n \"--ui-surface-muted\": \"#1e293b\", // slate-800\n \"--ui-overlay\": \"rgb(2 6 23 / 0.6)\", // slate-950 / 60%\n \"--ui-focus-ring\": \"0 0 0 3px rgb(99 102 241 / 0.45)\",\n};\n\n/**\n * Historically a shared `:host { --ui-x: var(--ui-x, fallback); } ` block,\n * re-declaring every token on each component's own host element as a\n * \"materialize the inherited value, or this fallback\" trick. That pattern\n * turned out to be a genuine bug, not a working default: browsers treat\n * `--ui-x: var(--ui-x, ...)` on `:host` as a self-referencing declaration\n * that computes to the guaranteed-invalid value REGARDLESS of what an\n * ancestor (e.g. `:root`) set — the host's own `:host` rule wins the cascade\n * over the ancestor's, so this dropped whatever the app actually set and\n * silently substituted the component's own fallback everywhere. It went\n * unnoticed because every component's fallback happened to equal the\n * intended light-mode value — dark mode (a real, different value at\n * `:root`) exposed it immediately (colors rendering as if no theme were\n * applied at all). Fix: don't redeclare tokens on `:host` — plain\n * inheritance already carries `:root`'s values into every shadow root\n * correctly, and every component already has its own `var(--ui-x, fallback)`\n * at each point of use, so nothing here is needed for the \"zero external\n * CSS\" default to keep working. Kept as an empty export so the existing\n * `static override styles = [tokens, css\\`...\\`]` import in every component\n * keeps compiling.\n */\nexport const tokens = css``;\n"]}
|
package/dist/user-avatar.d.ts
CHANGED
|
@@ -14,9 +14,12 @@ export declare class UserAvatar extends LitElement {
|
|
|
14
14
|
src: string | null;
|
|
15
15
|
/** Source string for the fallback initial (e.g. a display name or email) — first character, uppercased. */
|
|
16
16
|
name: string | null;
|
|
17
|
-
/** Diameter in CSS pixels. */
|
|
18
|
-
size: number;
|
|
17
|
+
/** Diameter in CSS pixels, or a preset name (`xs`=18, `sm`=24, `md`=32, `lg`=48). */
|
|
18
|
+
size: string | number;
|
|
19
19
|
private _imgError;
|
|
20
|
+
private static readonly SIZE_PRESETS;
|
|
21
|
+
/** Resolves the `size` property (number or preset name) to a pixel diameter. */
|
|
22
|
+
private resolveSize;
|
|
20
23
|
protected willUpdate(changed: Map<string, unknown>): void;
|
|
21
24
|
render(): import("lit-html").TemplateResult<1>;
|
|
22
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-avatar.d.ts","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C;;;;;;;;GAQG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,OAAgB,MAAM,4BAyBpB;IAEF,mFAAmF;IACvE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAQ;IACtC,2GAA2G;IAC/F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,
|
|
1
|
+
{"version":3,"file":"user-avatar.d.ts","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C;;;;;;;;GAQG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,OAAgB,MAAM,4BAyBpB;IAEF,mFAAmF;IACvE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAQ;IACtC,2GAA2G;IAC/F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,qFAAqF;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAM;IAE9B,OAAO,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAKlC;IAEF,gFAAgF;IAChF,OAAO,CAAC,WAAW;IAMnB,UAAmB,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,QAE1D;IAEQ,MAAM,yCAiBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
|
package/dist/user-avatar.js
CHANGED
|
@@ -4,6 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
+
var UserAvatar_1;
|
|
7
8
|
import { LitElement, css, html } from "lit";
|
|
8
9
|
import { customElement, property, state } from "lit/decorators.js";
|
|
9
10
|
import { iconUserCircle } from "./icons.js";
|
|
@@ -24,10 +25,11 @@ let UserAvatar = class UserAvatar extends LitElement {
|
|
|
24
25
|
this.src = null;
|
|
25
26
|
/** Source string for the fallback initial (e.g. a display name or email) — first character, uppercased. */
|
|
26
27
|
this.name = null;
|
|
27
|
-
/** Diameter in CSS pixels. */
|
|
28
|
+
/** Diameter in CSS pixels, or a preset name (`xs`=18, `sm`=24, `md`=32, `lg`=48). */
|
|
28
29
|
this.size = 32;
|
|
29
30
|
this._imgError = false;
|
|
30
31
|
}
|
|
32
|
+
static { UserAvatar_1 = this; }
|
|
31
33
|
static { this.styles = [
|
|
32
34
|
tokens,
|
|
33
35
|
css `
|
|
@@ -54,16 +56,30 @@ let UserAvatar = class UserAvatar extends LitElement {
|
|
|
54
56
|
}
|
|
55
57
|
`,
|
|
56
58
|
]; }
|
|
59
|
+
static { this.SIZE_PRESETS = {
|
|
60
|
+
xs: 18,
|
|
61
|
+
sm: 24,
|
|
62
|
+
md: 32,
|
|
63
|
+
lg: 48,
|
|
64
|
+
}; }
|
|
65
|
+
/** Resolves the `size` property (number or preset name) to a pixel diameter. */
|
|
66
|
+
resolveSize() {
|
|
67
|
+
const numeric = Number(this.size);
|
|
68
|
+
if (!Number.isNaN(numeric))
|
|
69
|
+
return numeric;
|
|
70
|
+
return UserAvatar_1.SIZE_PRESETS[String(this.size)] ?? 32;
|
|
71
|
+
}
|
|
57
72
|
willUpdate(changed) {
|
|
58
73
|
if (changed.has("src"))
|
|
59
74
|
this._imgError = false;
|
|
60
75
|
}
|
|
61
76
|
render() {
|
|
62
|
-
const
|
|
77
|
+
const size = this.resolveSize();
|
|
78
|
+
const dim = `${size}px`;
|
|
63
79
|
const showImage = !!this.src && !this._imgError;
|
|
64
80
|
const initial = this.name?.trim()[0]?.toUpperCase();
|
|
65
81
|
return html `
|
|
66
|
-
<span class="avatar" style="width:${dim};height:${dim};font-size:${
|
|
82
|
+
<span class="avatar" style="width:${dim};height:${dim};font-size:${size * 0.42}px">
|
|
67
83
|
${showImage
|
|
68
84
|
? html `<img
|
|
69
85
|
src=${this.src}
|
|
@@ -71,7 +87,7 @@ let UserAvatar = class UserAvatar extends LitElement {
|
|
|
71
87
|
referrerpolicy="no-referrer"
|
|
72
88
|
@error=${() => (this._imgError = true)}
|
|
73
89
|
/>`
|
|
74
|
-
: initial || iconUserCircle(Math.round(
|
|
90
|
+
: initial || iconUserCircle(Math.round(size * 0.65))}
|
|
75
91
|
</span>
|
|
76
92
|
`;
|
|
77
93
|
}
|
|
@@ -83,12 +99,12 @@ __decorate([
|
|
|
83
99
|
property()
|
|
84
100
|
], UserAvatar.prototype, "name", void 0);
|
|
85
101
|
__decorate([
|
|
86
|
-
property(
|
|
102
|
+
property()
|
|
87
103
|
], UserAvatar.prototype, "size", void 0);
|
|
88
104
|
__decorate([
|
|
89
105
|
state()
|
|
90
106
|
], UserAvatar.prototype, "_imgError", void 0);
|
|
91
|
-
UserAvatar = __decorate([
|
|
107
|
+
UserAvatar = UserAvatar_1 = __decorate([
|
|
92
108
|
customElement("user-avatar")
|
|
93
109
|
], UserAvatar);
|
|
94
110
|
export { UserAvatar };
|
package/dist/user-avatar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-avatar.js","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"user-avatar.js","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;GAQG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QA4BL,mFAAmF;QACvE,QAAG,GAAkB,IAAI,CAAC;QACtC,2GAA2G;QAC/F,SAAI,GAAkB,IAAI,CAAC;QACvC,qFAAqF;QACzE,SAAI,GAAoB,EAAE,CAAC;QAEtB,cAAS,GAAG,KAAK,CAAC;IAsCrC,CAAC;;aAxEiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;KAsBF;KACF,AAzBqB,CAyBpB;aAWsB,iBAAY,GAA2B;QAC7D,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;KACP,AALmC,CAKlC;IAEF,gFAAgF;IACxE,WAAW;QACjB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QAC3C,OAAO,aAAW,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,CAAC;IAEkB,UAAU,CAAC,OAA6B;QACzD,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACjD,CAAC;IAEQ,MAAM;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC;QACxB,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACpD,OAAO,IAAI,CAAA;0CAC2B,GAAG,WAAW,GAAG,cAAc,IAAI,GAAG,IAAI;UAC1E,SAAS;YACT,CAAC,CAAC,IAAI,CAAA;oBACI,IAAI,CAAC,GAAI;;;uBAGN,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;eACrC;YACL,CAAC,CAAC,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;;KAEzD,CAAC;IACJ,CAAC;CACF,CAAA;AA5Ca;IAAX,QAAQ,EAAE;uCAA2B;AAE1B;IAAX,QAAQ,EAAE;wCAA4B;AAE3B;IAAX,QAAQ,EAAE;wCAA4B;AAEtB;IAAhB,KAAK,EAAE;6CAA2B;AAnCxB,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAyEtB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { iconUserCircle } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * Circular avatar. Shows `src` when it loads successfully; falls back to the\n * first letter of `name` (uppercased) if `src` is unset or fails to load\n * (e.g. an expired OAuth profile-photo URL); falls back further to a generic\n * person icon if `name` is also unset. A broken image never leaves a blank\n * circle.\n *\n * @element user-avatar\n */\n@customElement(\"user-avatar\")\nexport class UserAvatar extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .avatar {\n border-radius: 50%;\n background: var(--ui-primary, #4f46e5);\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n font-weight: 600;\n overflow: hidden;\n flex: 0 0 auto;\n }\n img {\n width: 100%;\n height: 100%;\n border-radius: 50%;\n object-fit: cover;\n }\n `,\n ];\n\n /** Image URL to show. Falls back to initials/icon if unset or it fails to load. */\n @property() src: string | null = null;\n /** Source string for the fallback initial (e.g. a display name or email) — first character, uppercased. */\n @property() name: string | null = null;\n /** Diameter in CSS pixels, or a preset name (`xs`=18, `sm`=24, `md`=32, `lg`=48). */\n @property() size: string | number = 32;\n\n @state() private _imgError = false;\n\n private static readonly SIZE_PRESETS: Record<string, number> = {\n xs: 18,\n sm: 24,\n md: 32,\n lg: 48,\n };\n\n /** Resolves the `size` property (number or preset name) to a pixel diameter. */\n private resolveSize(): number {\n const numeric = Number(this.size);\n if (!Number.isNaN(numeric)) return numeric;\n return UserAvatar.SIZE_PRESETS[String(this.size)] ?? 32;\n }\n\n protected override willUpdate(changed: Map<string, unknown>) {\n if (changed.has(\"src\")) this._imgError = false;\n }\n\n override render() {\n const size = this.resolveSize();\n const dim = `${size}px`;\n const showImage = !!this.src && !this._imgError;\n const initial = this.name?.trim()[0]?.toUpperCase();\n return html`\n <span class=\"avatar\" style=\"width:${dim};height:${dim};font-size:${size * 0.42}px\">\n ${showImage\n ? html`<img\n src=${this.src!}\n alt=\"\"\n referrerpolicy=\"no-referrer\"\n @error=${() => (this._imgError = true)}\n />`\n : initial || iconUserCircle(Math.round(size * 0.65))}\n </span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"user-avatar\": UserAvatar;\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type DurationFormat = "seconds" | "compact";
|
|
2
|
+
/**
|
|
3
|
+
* Formats a millisecond duration for display.
|
|
4
|
+
*
|
|
5
|
+
* `"seconds"` renders a whole-second count with a pluralized unit word (e.g.
|
|
6
|
+
* "1 second", "12 seconds"). `"compact"` renders the shortest sensible
|
|
7
|
+
* `Xh Ym Zs` form, dropping leading-zero units (`"12s"`, `"3m 12s"`,
|
|
8
|
+
* `"1h 03m 12s"`) — minutes/seconds are zero-padded only once a larger unit
|
|
9
|
+
* is present.
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatDuration(ms: number, format: DurationFormat): string;
|
|
12
|
+
//# sourceMappingURL=duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../src/utils/duration.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC;AAMnD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAWzE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function pad2(n) {
|
|
2
|
+
return n < 10 ? `0${n}` : `${n}`;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Formats a millisecond duration for display.
|
|
6
|
+
*
|
|
7
|
+
* `"seconds"` renders a whole-second count with a pluralized unit word (e.g.
|
|
8
|
+
* "1 second", "12 seconds"). `"compact"` renders the shortest sensible
|
|
9
|
+
* `Xh Ym Zs` form, dropping leading-zero units (`"12s"`, `"3m 12s"`,
|
|
10
|
+
* `"1h 03m 12s"`) — minutes/seconds are zero-padded only once a larger unit
|
|
11
|
+
* is present.
|
|
12
|
+
*/
|
|
13
|
+
export function formatDuration(ms, format) {
|
|
14
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
|
15
|
+
if (format === "seconds") {
|
|
16
|
+
return totalSeconds === 1 ? "1 second" : `${totalSeconds} seconds`;
|
|
17
|
+
}
|
|
18
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
19
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
20
|
+
const seconds = totalSeconds % 60;
|
|
21
|
+
if (hours > 0)
|
|
22
|
+
return `${hours}h ${pad2(minutes)}m ${pad2(seconds)}s`;
|
|
23
|
+
if (minutes > 0)
|
|
24
|
+
return `${minutes}m ${pad2(seconds)}s`;
|
|
25
|
+
return `${seconds}s`;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../../src/utils/duration.ts"],"names":[],"mappings":"AAEA,SAAS,IAAI,CAAC,CAAS;IACrB,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,MAAsB;IAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACxD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,YAAY,UAAU,CAAC;IACrE,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,GAAG,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;IACtE,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,GAAG,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;IACxD,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC","sourcesContent":["export type DurationFormat = \"seconds\" | \"compact\";\n\nfunction pad2(n: number): string {\n return n < 10 ? `0${n}` : `${n}`;\n}\n\n/**\n * Formats a millisecond duration for display.\n *\n * `\"seconds\"` renders a whole-second count with a pluralized unit word (e.g.\n * \"1 second\", \"12 seconds\"). `\"compact\"` renders the shortest sensible\n * `Xh Ym Zs` form, dropping leading-zero units (`\"12s\"`, `\"3m 12s\"`,\n * `\"1h 03m 12s\"`) — minutes/seconds are zero-padded only once a larger unit\n * is present.\n */\nexport function formatDuration(ms: number, format: DurationFormat): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n if (format === \"seconds\") {\n return totalSeconds === 1 ? \"1 second\" : `${totalSeconds} seconds`;\n }\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) return `${hours}h ${pad2(minutes)}m ${pad2(seconds)}s`;\n if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;\n return `${seconds}s`;\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# `<chat-message>`
|
|
2
|
+
|
|
3
|
+
One conversation entry in a chat-style activity feed. Tool calls and
|
|
4
|
+
"thinking" traces are variants of this component rather than separate
|
|
5
|
+
ones — they share the same header, collapse behavior, and body card as a
|
|
6
|
+
normal message, just dimmed and collapsible with an always-visible summary.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import "@f-ewald/components/chat-message.js";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<chat-message role="user" author="Freddy" timestamp="2026-07-19T12:00:00Z">
|
|
18
|
+
Write notes.md containing a haiku.
|
|
19
|
+
</chat-message>
|
|
20
|
+
<chat-message role="agent" variant="tool" collapsible collapsed summary='file_write · {"filename": "notes.md"}'>
|
|
21
|
+
directory: .
|
|
22
|
+
filename: notes.md
|
|
23
|
+
</chat-message>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Attributes / properties
|
|
27
|
+
|
|
28
|
+
| Property | Attribute | Type | Default | Description |
|
|
29
|
+
| --- | --- | --- | --- | --- |
|
|
30
|
+
| `role` | `role` | `ChatMessageRole` | `"agent"` | Whose message this is; drives the card background/border. |
|
|
31
|
+
| `variant` | `variant` | `ChatMessageVariant` | `"normal"` | `tool`/`thinking` render dimmed and smaller, with `tool` using monospace for its body. |
|
|
32
|
+
| `author` | `author` | `string` | `""` | Header label, e.g. "Freddy" or "Architect". |
|
|
33
|
+
| `timestamp` | `timestamp` | `string | null` | `null` | ISO-8601 timestamp, rendered via `relative-time` in the header. |
|
|
34
|
+
| `summary` | `summary` | `string` | `""` | Always-visible one-liner for `tool`/`thinking` variants (e.g. a truncated args preview). |
|
|
35
|
+
| `collapsible` | `collapsible` | `boolean` | `false` | Whether clicking the header/summary toggles the body slot. |
|
|
36
|
+
| `collapsed` | `collapsed` | `boolean` | `false` | Current collapse state (reflected as an attribute). |
|
|
37
|
+
|
|
38
|
+
## Events
|
|
39
|
+
|
|
40
|
+
| Event | Description |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `toggle` | Fired with `{ collapsed: boolean }` when the header/summary is clicked in collapsible mode. |
|
|
43
|
+
|
|
44
|
+
## Slots
|
|
45
|
+
|
|
46
|
+
_None._
|
|
47
|
+
|
|
48
|
+
## CSS custom properties
|
|
49
|
+
|
|
50
|
+
| Custom property |
|
|
51
|
+
| --- |
|
|
52
|
+
| `--ui-border` |
|
|
53
|
+
| `--ui-font` |
|
|
54
|
+
| `--ui-font-size` |
|
|
55
|
+
| `--ui-primary` |
|
|
56
|
+
| `--ui-radius` |
|
|
57
|
+
| `--ui-surface` |
|
|
58
|
+
| `--ui-text` |
|
|
59
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# `<data-table>`
|
|
2
|
+
|
|
3
|
+
A generic, presentational table shell: renders a `<thead>` from `columns`
|
|
4
|
+
and one `<tr>` per entry in `rows`, with each cell's content produced by
|
|
5
|
+
`renderCell` (default: plain property lookup on the row object). Knows
|
|
6
|
+
nothing about what a "row" means — callers own the data shape entirely.
|
|
7
|
+
|
|
8
|
+
Optional `rowHref` makes whole rows clickable (navigating via `location.hash`),
|
|
9
|
+
without hijacking clicks on nested interactive elements (links/buttons) a
|
|
10
|
+
cell's rendered content might contain.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import "@f-ewald/components/data-table.js";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<data-table></data-table>
|
|
22
|
+
<script type="module">
|
|
23
|
+
const table = document.querySelector("data-table");
|
|
24
|
+
table.columns = [
|
|
25
|
+
{ key: "title", label: "Title" },
|
|
26
|
+
{ key: "state", label: "State" },
|
|
27
|
+
];
|
|
28
|
+
table.rows = [
|
|
29
|
+
{ id: "tsk_1", title: "Write onboarding docs", state: "Backlog" },
|
|
30
|
+
{ id: "tsk_2", title: "Fix the login bug", state: "Done" },
|
|
31
|
+
];
|
|
32
|
+
table.rowHref = (row) => `#/tasks/${row.id}`;
|
|
33
|
+
</script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Attributes / properties
|
|
37
|
+
|
|
38
|
+
| Property | Attribute | Type | Default | Description |
|
|
39
|
+
| --- | --- | --- | --- | --- |
|
|
40
|
+
| `columns` | _(JS property only)_ | `DataTableColumn[]` | `[]` | Column headers, in display order. |
|
|
41
|
+
| `rows` | _(JS property only)_ | `unknown[]` | `[]` | Row data; opaque to this component beyond what `renderCell` does with it. |
|
|
42
|
+
| `rowKey` | _(JS property only)_ | `(row: unknown, index: number) => string | number` | `—` | Stable identity for `rows[i]`, used as the repeat-directive key. Defaults to the row's index. |
|
|
43
|
+
| `renderCell` | _(JS property only)_ | `(row: unknown, key: string) => unknown` | `—` | Produces a cell's rendered content for `row`/`column.key`. Default: `row[key]`. |
|
|
44
|
+
| `rowHref` | _(JS property only)_ | `((row: unknown) => string | null) | null` | `null` | When set, clicking a row (outside any nested link/button) navigates to this hash. |
|
|
45
|
+
|
|
46
|
+
## Events
|
|
47
|
+
|
|
48
|
+
_None._
|
|
49
|
+
|
|
50
|
+
## Slots
|
|
51
|
+
|
|
52
|
+
_None._
|
|
53
|
+
|
|
54
|
+
## CSS custom properties
|
|
55
|
+
|
|
56
|
+
| Custom property |
|
|
57
|
+
| --- |
|
|
58
|
+
| `--ui-border` |
|
|
59
|
+
| `--ui-font` |
|
|
60
|
+
| `--ui-font-size-sm` |
|
|
61
|
+
| `--ui-surface-muted` |
|
|
62
|
+
| `--ui-text` |
|
|
63
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# `<dropdown-button>`
|
|
2
|
+
|
|
3
|
+
A primary-styled button with a label and chevron that opens an anchored
|
|
4
|
+
menu of actions — essentially `form-select` minus "current value"
|
|
5
|
+
semantics: a menu, not a select. Use for a set of mutually exclusive
|
|
6
|
+
next-step actions (e.g. a failed task's Retry / Close / Backlog).
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import "@f-ewald/components/dropdown-button.js";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<dropdown-button label="Resolve…"></dropdown-button>
|
|
18
|
+
<script type="module">
|
|
19
|
+
const dropdown = document.querySelector("dropdown-button");
|
|
20
|
+
dropdown.options = [
|
|
21
|
+
{ value: "retry", label: "Retry" },
|
|
22
|
+
{ value: "close", label: "Close" },
|
|
23
|
+
{ value: "backlog", label: "Backlog" },
|
|
24
|
+
];
|
|
25
|
+
dropdown.addEventListener("select", (e) => console.log(e.detail.value));
|
|
26
|
+
</script>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Attributes / properties
|
|
30
|
+
|
|
31
|
+
| Property | Attribute | Type | Default | Description |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `label` | `label` | `string` | `""` | The trigger button's label. |
|
|
34
|
+
| `options` | _(JS property only)_ | `DropdownOption[]` | `[]` | The menu's actions. |
|
|
35
|
+
| `disabled` | `disabled` | `boolean` | `false` | Disables the trigger, preventing the menu from opening. |
|
|
36
|
+
|
|
37
|
+
## Events
|
|
38
|
+
|
|
39
|
+
| Event | Description |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `select` | Fired with `{ value: string }` when a menu item is picked. |
|
|
42
|
+
|
|
43
|
+
## Slots
|
|
44
|
+
|
|
45
|
+
_None._
|
|
46
|
+
|
|
47
|
+
## CSS custom properties
|
|
48
|
+
|
|
49
|
+
| Custom property |
|
|
50
|
+
| --- |
|
|
51
|
+
| `--ui-border` |
|
|
52
|
+
| `--ui-focus-ring` |
|
|
53
|
+
| `--ui-font` |
|
|
54
|
+
| `--ui-font-size-sm` |
|
|
55
|
+
| `--ui-primary` |
|
|
56
|
+
| `--ui-primary-hover` |
|
|
57
|
+
| `--ui-radius-sm` |
|
|
58
|
+
| `--ui-shadow` |
|
|
59
|
+
| `--ui-surface` |
|
|
60
|
+
| `--ui-surface-muted` |
|
|
61
|
+
| `--ui-text` |
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# `<editable-text>`
|
|
2
|
+
|
|
3
|
+
Jira/GitHub-style click-to-edit text: a display span that turns into an
|
|
4
|
+
`<input>` (or auto-growing `<textarea>` when `multiline`) on click. The
|
|
5
|
+
input/textarea inherits the host's font, so a title wrapped in an `<h1>`
|
|
6
|
+
edits at title size.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import "@f-ewald/components/editable-text.js";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<editable-text value="Write the quarterly report" label="Title"></editable-text>
|
|
18
|
+
<editable-text multiline placeholder="Add a description…" label="Description"></editable-text>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Attributes / properties
|
|
22
|
+
|
|
23
|
+
| Property | Attribute | Type | Default | Description |
|
|
24
|
+
| --- | --- | --- | --- | --- |
|
|
25
|
+
| `value` | `value` | `string` | `""` | Current text. |
|
|
26
|
+
| `multiline` | `multiline` | `boolean` | `false` | `false` renders an `<input>`, `true` an auto-growing `<textarea>`. |
|
|
27
|
+
| `placeholder` | `placeholder` | `string` | `""` | Muted placeholder text shown when `value` is empty, and as the input's placeholder. |
|
|
28
|
+
| `readonly` | `readonly` | `boolean` | `false` | Disables entering edit mode. |
|
|
29
|
+
| `label` | `label` | `string` | `""` | `aria-label` applied to the input/textarea. |
|
|
30
|
+
|
|
31
|
+
## Events
|
|
32
|
+
|
|
33
|
+
| Event | Description |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `change` | Fired with `{ value: string }` when the committed value differs from the previous one. |
|
|
36
|
+
|
|
37
|
+
## Slots
|
|
38
|
+
|
|
39
|
+
_None._
|
|
40
|
+
|
|
41
|
+
## CSS custom properties
|
|
42
|
+
|
|
43
|
+
| Custom property |
|
|
44
|
+
| --- |
|
|
45
|
+
| `--ui-border` |
|
|
46
|
+
| `--ui-focus-ring` |
|
|
47
|
+
| `--ui-primary` |
|
|
48
|
+
| `--ui-radius-sm` |
|
|
49
|
+
| `--ui-surface` |
|
|
50
|
+
| `--ui-surface-muted` |
|
|
51
|
+
| `--ui-text-muted` |
|