@llumi/design-system 1.0.1 → 2.0.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.
Files changed (50) hide show
  1. package/LICENSE +51 -0
  2. package/NOTICE +66 -0
  3. package/README.md +15 -205
  4. package/custom-elements.json +1135 -18
  5. package/dist/LICENSE +51 -0
  6. package/dist/NOTICE +66 -0
  7. package/dist/README.md +29 -0
  8. package/dist/adopt-global-styles-DykTxdm8.js +15 -0
  9. package/dist/base.css +2 -0
  10. package/dist/base.md +59 -0
  11. package/dist/bpmn.md +113 -0
  12. package/dist/bpmn.mjs +5076 -0
  13. package/dist/copy-button.md +128 -0
  14. package/dist/copy-button.mjs +161 -0
  15. package/dist/download-button.md +106 -0
  16. package/dist/download-button.mjs +177 -0
  17. package/dist/highlighter.md +45 -0
  18. package/dist/highlighter.mjs +403 -0
  19. package/dist/icon-button-BeLaqJmK.js +5 -0
  20. package/dist/icons-DBLWcA83.js +42 -0
  21. package/dist/index-elWLeWLs.js +7733 -0
  22. package/dist/llumi-element-DTeRDxao.js +22 -0
  23. package/dist/mermaid.md +58 -0
  24. package/dist/mermaid.mjs +254 -0
  25. package/dist/review.md +141 -0
  26. package/dist/review.mjs +1030 -696
  27. package/dist/slot-text-DW3pDxNK.js +51 -0
  28. package/dist/theme.css +2 -0
  29. package/package.json +73 -49
  30. package/dist/review.cjs +0 -172
  31. package/dist/review.css +0 -98
  32. package/dist/review.js +0 -172
  33. package/dist/types/components/review/comment-dialog.element.d.ts +0 -29
  34. package/dist/types/components/review/custom-target.utils.d.ts +0 -16
  35. package/dist/types/components/review/default-editor.element.d.ts +0 -19
  36. package/dist/types/components/review/highlight-manager.d.ts +0 -21
  37. package/dist/types/components/review/index.d.ts +0 -8
  38. package/dist/types/components/review/overall-comment-dialog.element.d.ts +0 -22
  39. package/dist/types/components/review/review-bar.element.d.ts +0 -26
  40. package/dist/types/components/review/review.element.d.ts +0 -60
  41. package/dist/types/components/review/review.machine.d.ts +0 -234
  42. package/dist/types/components/review/review.persistence.d.ts +0 -16
  43. package/dist/types/components/review/review.type.d.ts +0 -37
  44. package/dist/types/components/review/selection-comment-icons.element.d.ts +0 -17
  45. package/dist/types/components/review/selection-range.utils.d.ts +0 -6
  46. package/dist/types/components/review/sort-comments.utils.d.ts +0 -6
  47. package/dist/types/index.d.ts +0 -1
  48. package/dist/types/shared/cn.d.ts +0 -2
  49. package/dist/types/shared/icons.d.ts +0 -7
  50. package/dist/types/shared/tailwind-element.d.ts +0 -4
@@ -0,0 +1,128 @@
1
+ # `<llumi-copy-button>`
2
+
3
+ Copy-to-clipboard icon button with animated success/error feedback.
4
+
5
+ ## What it is
6
+
7
+ A minimal icon button that copies text to the clipboard and shows live state transitions:
8
+
9
+ | State | Visual |
10
+ |-------|--------|
11
+ | idle | copy icon |
12
+ | copying | spinning indicator (button disabled) |
13
+ | copied | green ✓ icon, resets after 1 s |
14
+ | error | red × icon, resets after 1 s |
15
+
16
+ ---
17
+
18
+ ## Usage
19
+
20
+ > **Requires the base theme.** This is a light-DOM element styled by the design-system stylesheet — load `@llumi/design-system/base.css` (or `base.css`) on the page, or the button renders unstyled.
21
+
22
+ ```html
23
+ <!-- 1. Register the element (auto side-effect) -->
24
+ <script type="module">
25
+ import "@llumi/design-system/copy-button";
26
+ </script>
27
+
28
+ <!-- 2. Drop the tag wherever you need it -->
29
+ <llumi-copy-button value="Text to copy"></llumi-copy-button>
30
+ ```
31
+
32
+ Or with named import:
33
+
34
+ ```js
35
+ import { LlumiCopyButton } from "@llumi/design-system/copy-button";
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Copy source precedence
41
+
42
+ The text that gets written to the clipboard is resolved from the first source that is available, in this order:
43
+
44
+ 1. **`getValue` (JS function property)** — highest precedence. Assign a function:
45
+ ```js
46
+ document.querySelector("llumi-copy-button").getValue = () => editor.getValue();
47
+ ```
48
+ 2. **`get-value` (attribute)** — a dotted path to a function on `globalThis`, resolved at click time via plain property traversal (no `eval`, no `new Function`). The component warns in the console if the path does not resolve to a function:
49
+ ```html
50
+ <llumi-copy-button get-value="myApp.getSnippet"></llumi-copy-button>
51
+ ```
52
+ ```js
53
+ window.myApp = { getSnippet: () => editor.getValue() };
54
+ ```
55
+ 3. **`value` (string attribute)** — a plain, static string:
56
+ ```html
57
+ <llumi-copy-button value="npm install @llumi/design-system"></llumi-copy-button>
58
+ ```
59
+ 4. **`""` (empty string)** — fallback when none of the above is set.
60
+
61
+ ---
62
+
63
+ ## Attributes and properties
64
+
65
+ | Name | Type | Default | Notes |
66
+ |------|------|---------|-------|
67
+ | `value` | `string` attr + prop | `""` | Static text to copy. Lowest-precedence source. |
68
+ | `title` | `string` attr + prop | `"Copy to clipboard"` | Native tooltip; mirrored to `aria-label`. |
69
+ | `getValue` | `() => string` prop only | `undefined` | JS function property. No HTML attribute equivalent. Highest-precedence source. |
70
+ | `get-value` | `string` attr | — | Dotted path on `globalThis` resolving to a function. Resolved at click time. |
71
+ | `disabled` | — | — | Managed internally while copying; do not set manually. |
72
+
73
+ ---
74
+
75
+ ## Events
76
+
77
+ Both events bubble and are composed.
78
+
79
+ ### `llumi-copy`
80
+
81
+ Fires on a successful clipboard write.
82
+
83
+ ```ts
84
+ element.addEventListener("llumi-copy", (e: CustomEvent<{ text: string }>) => {
85
+ console.log("Copied:", e.detail.text);
86
+ });
87
+ ```
88
+
89
+ ### `llumi-copy-error`
90
+
91
+ Fires when `navigator.clipboard.writeText` rejects (e.g. permission denied in a non-HTTPS context or missing user gesture).
92
+
93
+ ```ts
94
+ element.addEventListener("llumi-copy-error", (e: CustomEvent<{ error: unknown }>) => {
95
+ console.error("Copy failed:", e.detail.error);
96
+ });
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Styling
102
+
103
+ ### Styling the button (light DOM)
104
+
105
+ The component renders in **light DOM**, so the inner `<button>` is a normal,
106
+ fully styleable element — no shadow piercing required:
107
+
108
+ ```css
109
+ llumi-copy-button button {
110
+ width: 3rem;
111
+ height: 3rem;
112
+ border-radius: 9999px;
113
+ }
114
+ ```
115
+
116
+ ### CSS custom properties
117
+
118
+ Each token has a sensible `oklch` fallback, so the component is usable without a design-system theme.
119
+
120
+ | Token | Role | Fallback |
121
+ |-------|------|---------|
122
+ | `--llumi-color-success` | Border + icon colour in the copied state | `oklch(62.7% 0.194 149.214)` (green) |
123
+ | `--llumi-color-danger` | Border + icon colour in the error state | `oklch(57.7% 0.245 27.325)` (red) |
124
+ | `--llumi-color-surface` | Button background | `oklch(100% 0 0)` (white) |
125
+ | `--llumi-color-border` | Button border | `oklch(92% 0.004 286.32)` (light grey) |
126
+ | `--llumi-color-fg` | Icon colour (idle) | `oklch(21% 0.006 285.885)` (near-black) |
127
+ | `--llumi-color-ring` | Focus-visible outline | `oklch(55.8% 0.288 302.321)` (purple) |
128
+ | `--llumi-radius` | Button border-radius | `0.5rem` |
@@ -0,0 +1,161 @@
1
+ import { html as d } from "https://cdn.jsdelivr.net/npm/lit@3.3.3";
2
+ import { property as u, customElement as h } from "https://cdn.jsdelivr.net/npm/lit@3.3.3/decorators.js";
3
+ import { i as s } from "./icons-DBLWcA83.js";
4
+ import { L as b } from "./llumi-element-DTeRDxao.js";
5
+ import { defineMachine as m } from "https://cdn.jsdelivr.net/npm/yay-machine@1.5.0";
6
+ /*! @llumi/design-system v2.0.0 | (c) 2026 pAIrprog SAS | license: https://cdn.jsdelivr.net/npm/@llumi/design-system@2.0.0/LICENSE | bundled third-party components under MIT, see NOTICE | @license */
7
+ const p = 1e3, f = m({
8
+ initialState: { name: "idle" },
9
+ states: {
10
+ idle: {
11
+ on: {
12
+ COPY: { to: "copying", data: ({ event: t }) => ({ text: t.text }) }
13
+ }
14
+ },
15
+ copying: {
16
+ onEnter: ({ state: t, send: e }) => {
17
+ navigator.clipboard.writeText(t.text).then(() => e({ type: "COPY_SUCCESS", text: t.text })).catch((o) => e({ type: "COPY_ERROR", error: o }));
18
+ },
19
+ on: {
20
+ COPY_SUCCESS: { to: "copied" },
21
+ COPY_ERROR: { to: "error" }
22
+ }
23
+ },
24
+ copied: {
25
+ onEnter: ({ send: t }) => {
26
+ const e = setTimeout(() => t({ type: "DONE" }), p);
27
+ return () => clearTimeout(e);
28
+ },
29
+ on: {
30
+ DONE: { to: "idle" },
31
+ COPY: { to: "copying", data: ({ event: t }) => ({ text: t.text }) }
32
+ }
33
+ },
34
+ error: {
35
+ onEnter: ({ send: t }) => {
36
+ const e = setTimeout(() => t({ type: "DONE" }), p);
37
+ return () => clearTimeout(e);
38
+ },
39
+ on: {
40
+ DONE: { to: "idle" },
41
+ COPY: { to: "copying", data: ({ event: t }) => ({ text: t.text }) }
42
+ }
43
+ }
44
+ }
45
+ });
46
+ function y(t, e = globalThis) {
47
+ let o = e;
48
+ for (const r of t.split(".")) {
49
+ if (o == null) return;
50
+ o = o[r];
51
+ }
52
+ return o;
53
+ }
54
+ var C = Object.defineProperty, E = Object.getOwnPropertyDescriptor, c = (t, e, o, r) => {
55
+ for (var i = r > 1 ? void 0 : r ? E(e, o) : e, l = t.length - 1, a; l >= 0; l--)
56
+ (a = t[l]) && (i = (r ? a(e, o, i) : a(i)) || i);
57
+ return r && i && C(e, o, i), i;
58
+ };
59
+ function O(t) {
60
+ throw new Error(`Unexpected state: ${JSON.stringify(t)}`);
61
+ }
62
+ let n = class extends b {
63
+ constructor() {
64
+ super(...arguments), this.value = "", this.title = "Copy to clipboard";
65
+ }
66
+ /** Light DOM so the document-level base.css button style applies. */
67
+ createRenderRoot() {
68
+ return this;
69
+ }
70
+ connectedCallback() {
71
+ super.connectedCallback(), this.instance = f.newInstance().start(), this.unsubscribe = this.instance.subscribe(
72
+ ({ state: t, event: e }) => this.onTransition(t, e)
73
+ );
74
+ }
75
+ disconnectedCallback() {
76
+ var t, e;
77
+ super.disconnectedCallback(), (t = this.unsubscribe) == null || t.call(this), this.unsubscribe = void 0, (e = this.instance) == null || e.stop(), this.instance = void 0;
78
+ }
79
+ onTransition(t, e) {
80
+ (e == null ? void 0 : e.type) === "COPY_SUCCESS" ? this.dispatchEvent(
81
+ new CustomEvent("llumi-copy", {
82
+ detail: { text: e.text },
83
+ bubbles: !0,
84
+ composed: !0
85
+ })
86
+ ) : (e == null ? void 0 : e.type) === "COPY_ERROR" && this.dispatchEvent(
87
+ new CustomEvent("llumi-copy-error", {
88
+ detail: { error: e.error },
89
+ bubbles: !0,
90
+ composed: !0
91
+ })
92
+ ), this.requestUpdate();
93
+ }
94
+ /**
95
+ * Resolve the text to copy, by precedence:
96
+ * getValue() (function prop)
97
+ * → get-value (attribute: dotted path to a function on globalThis, resolved at click time)
98
+ * → value (string attr)
99
+ * → ""
100
+ */
101
+ resolveText() {
102
+ if (typeof this.getValue == "function") return this.getValue();
103
+ const t = this.getAttribute("get-value");
104
+ if (t) {
105
+ const e = y(t);
106
+ if (typeof e == "function")
107
+ return String(e());
108
+ console.warn(
109
+ `<llumi-copy-button>: get-value="${t}" did not resolve to a function on globalThis`
110
+ );
111
+ }
112
+ return this.value ?? "";
113
+ }
114
+ onClick() {
115
+ var t;
116
+ (t = this.instance) == null || t.send({ type: "COPY", text: this.resolveText() });
117
+ }
118
+ viewFor(t) {
119
+ switch (t) {
120
+ case "idle":
121
+ return { icon: s.copy, cls: "" };
122
+ case "copying":
123
+ return { icon: s.spinner, cls: "is-working" };
124
+ case "copied":
125
+ return { icon: s.check, cls: "is-done" };
126
+ case "error":
127
+ return { icon: s.x, cls: "is-error" };
128
+ default:
129
+ return O(t);
130
+ }
131
+ }
132
+ render() {
133
+ var o;
134
+ const t = ((o = this.instance) == null ? void 0 : o.state.name) ?? "idle", e = this.viewFor(t);
135
+ return d`<button
136
+ type="button"
137
+ class=${e.cls}
138
+ title=${this.title}
139
+ aria-label=${this.title}
140
+ ?disabled=${t === "copying"}
141
+ @click=${this.onClick}
142
+ >
143
+ <i>${e.icon}</i>
144
+ </button>`;
145
+ }
146
+ };
147
+ c([
148
+ u()
149
+ ], n.prototype, "value", 2);
150
+ c([
151
+ u()
152
+ ], n.prototype, "title", 2);
153
+ c([
154
+ u({ attribute: !1 })
155
+ ], n.prototype, "getValue", 2);
156
+ n = c([
157
+ h("llumi-copy-button")
158
+ ], n);
159
+ export {
160
+ n as LlumiCopyButton
161
+ };
@@ -0,0 +1,106 @@
1
+ # `<llumi-download-button>`
2
+
3
+ Download-a-string-as-a-file icon button with animated success/error feedback.
4
+
5
+ ## What it is
6
+
7
+ A minimal icon button that downloads a string as a file and shows live state:
8
+
9
+ | State | Visual |
10
+ |-------|--------|
11
+ | idle | download icon |
12
+ | downloading | spinning indicator (button disabled) |
13
+ | downloaded | green ✓ icon, resets after 1 s |
14
+ | error | red × icon, resets after 1 s |
15
+
16
+ ---
17
+
18
+ ## Usage
19
+
20
+ > **Requires the base theme.** This is a light-DOM element styled by the design-system stylesheet — load `@llumi/design-system/base.css` (or `base.css`) on the page, or the button renders unstyled.
21
+
22
+ ```html
23
+ <script type="module">
24
+ import "@llumi/design-system/download-button";
25
+ </script>
26
+
27
+ <llumi-download-button filename="data.json" mime-type="application/json"></llumi-download-button>
28
+ ```
29
+
30
+ Set the content via the `getValue` function property (Lit binding `.getValue=${…}`,
31
+ or assign it in JS):
32
+
33
+ ```js
34
+ const btn = document.querySelector("llumi-download-button");
35
+ btn.getValue = () => JSON.stringify(state, null, 2);
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Attributes and properties
41
+
42
+ | Name | Type | Default | Notes |
43
+ |------|------|---------|-------|
44
+ | `getValue` | `() => string` prop only | `undefined` | Returns the content to download. Called at click time. No HTML attribute. |
45
+ | `filename` | `string` attr + prop | `""` | **Required.** A click with no filename logs a `console.warn` and does nothing. |
46
+ | `mime-type` | `string` attr (prop `mimeType`) | `"text/plain"` | MIME type passed to the `Blob`. |
47
+ | `title` | `string` attr + prop | `"Download file"` | Native tooltip; mirrored to `aria-label`. |
48
+ | `disabled` | — | — | Managed internally while downloading; do not set manually. |
49
+
50
+ ---
51
+
52
+ ## Events
53
+
54
+ Both events bubble and are composed.
55
+
56
+ ### `llumi-download`
57
+
58
+ Fires after the file download is triggered.
59
+
60
+ ```ts
61
+ element.addEventListener("llumi-download", (e: CustomEvent<{ filename: string }>) => {
62
+ console.log("Downloaded:", e.detail.filename);
63
+ });
64
+ ```
65
+
66
+ ### `llumi-download-error`
67
+
68
+ Fires if building or triggering the download throws.
69
+
70
+ ```ts
71
+ element.addEventListener("llumi-download-error", (e: CustomEvent<{ error: unknown }>) => {
72
+ console.error("Download failed:", e.detail.error);
73
+ });
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Styling
79
+
80
+ The component renders in **light DOM**, so style the inner `<button>` directly —
81
+ no shadow piercing required:
82
+
83
+ ```css
84
+ llumi-download-button button {
85
+ width: 3rem;
86
+ height: 3rem;
87
+ border-radius: 9999px;
88
+ }
89
+ ```
90
+
91
+ The shared icon-button look (and `.is-done` / `.is-error` state colours) is defined
92
+ once in `base.css` and shared with `<llumi-copy-button>`.
93
+
94
+ ### CSS custom properties
95
+
96
+ Each token has a sensible `oklch` fallback.
97
+
98
+ | Token | Role | Fallback |
99
+ |-------|------|---------|
100
+ | `--llumi-color-success` | Border + icon colour in the downloaded state | `oklch(62.7% 0.194 149.214)` (green) |
101
+ | `--llumi-color-danger` | Border + icon colour in the error state | `oklch(57.7% 0.245 27.325)` (red) |
102
+ | `--llumi-color-surface` | Button background | `oklch(100% 0 0)` (white) |
103
+ | `--llumi-color-border` | Button border | `oklch(92% 0.004 286.32)` (light grey) |
104
+ | `--llumi-color-fg` | Icon colour (idle) | `oklch(21% 0.006 285.885)` (near-black) |
105
+ | `--llumi-color-ring` | Focus-visible outline | `oklch(55.8% 0.288 302.321)` (purple) |
106
+ | `--llumi-radius` | Button border-radius | `0.5rem` |
@@ -0,0 +1,177 @@
1
+ import { html as u } from "https://cdn.jsdelivr.net/npm/lit@3.3.3";
2
+ import { property as l, customElement as p } from "https://cdn.jsdelivr.net/npm/lit@3.3.3/decorators.js";
3
+ import { i as a } from "./icons-DBLWcA83.js";
4
+ import { L as f } from "./llumi-element-DTeRDxao.js";
5
+ import { defineMachine as h } from "https://cdn.jsdelivr.net/npm/yay-machine@1.5.0";
6
+ /*! @llumi/design-system v2.0.0 | (c) 2026 pAIrprog SAS | license: https://cdn.jsdelivr.net/npm/@llumi/design-system@2.0.0/LICENSE | bundled third-party components under MIT, see NOTICE | @license */
7
+ const d = 1e3, O = h({
8
+ initialState: { name: "idle" },
9
+ states: {
10
+ idle: {
11
+ on: {
12
+ DOWNLOAD: {
13
+ to: "downloading",
14
+ data: ({ event: e }) => ({
15
+ text: e.text,
16
+ filename: e.filename,
17
+ mimeType: e.mimeType
18
+ })
19
+ }
20
+ }
21
+ },
22
+ downloading: {
23
+ onEnter: ({ state: e, send: t }) => {
24
+ try {
25
+ const i = URL.createObjectURL(
26
+ new Blob([e.text], { type: e.mimeType })
27
+ ), o = document.createElement("a");
28
+ o.href = i, o.download = e.filename, o.click(), URL.revokeObjectURL(i), queueMicrotask(
29
+ () => t({ type: "DOWNLOAD_SUCCESS", filename: e.filename })
30
+ );
31
+ } catch (i) {
32
+ queueMicrotask(() => t({ type: "DOWNLOAD_ERROR", error: i }));
33
+ }
34
+ },
35
+ on: {
36
+ DOWNLOAD_SUCCESS: { to: "downloaded" },
37
+ DOWNLOAD_ERROR: { to: "error" }
38
+ }
39
+ },
40
+ downloaded: {
41
+ onEnter: ({ send: e }) => {
42
+ const t = setTimeout(() => e({ type: "DONE" }), d);
43
+ return () => clearTimeout(t);
44
+ },
45
+ on: {
46
+ DONE: { to: "idle" },
47
+ DOWNLOAD: {
48
+ to: "downloading",
49
+ data: ({ event: e }) => ({
50
+ text: e.text,
51
+ filename: e.filename,
52
+ mimeType: e.mimeType
53
+ })
54
+ }
55
+ }
56
+ },
57
+ error: {
58
+ onEnter: ({ send: e }) => {
59
+ const t = setTimeout(() => e({ type: "DONE" }), d);
60
+ return () => clearTimeout(t);
61
+ },
62
+ on: {
63
+ DONE: { to: "idle" },
64
+ DOWNLOAD: {
65
+ to: "downloading",
66
+ data: ({ event: e }) => ({
67
+ text: e.text,
68
+ filename: e.filename,
69
+ mimeType: e.mimeType
70
+ })
71
+ }
72
+ }
73
+ }
74
+ }
75
+ });
76
+ var b = Object.defineProperty, w = Object.getOwnPropertyDescriptor, s = (e, t, i, o) => {
77
+ for (var n = o > 1 ? void 0 : o ? w(t, i) : t, c = e.length - 1, m; c >= 0; c--)
78
+ (m = e[c]) && (n = (o ? m(t, i, n) : m(n)) || n);
79
+ return o && n && b(t, i, n), n;
80
+ };
81
+ function y(e) {
82
+ throw new Error(`Unexpected state: ${JSON.stringify(e)}`);
83
+ }
84
+ let r = class extends f {
85
+ constructor() {
86
+ super(...arguments), this.filename = "", this.mimeType = "text/plain", this.title = "Download file";
87
+ }
88
+ /** Light DOM so the document-level base.css button style applies. */
89
+ createRenderRoot() {
90
+ return this;
91
+ }
92
+ connectedCallback() {
93
+ super.connectedCallback(), this.instance = O.newInstance().start(), this.unsubscribe = this.instance.subscribe(
94
+ ({ state: e, event: t }) => this.onTransition(e, t)
95
+ );
96
+ }
97
+ disconnectedCallback() {
98
+ var e, t;
99
+ super.disconnectedCallback(), (e = this.unsubscribe) == null || e.call(this), this.unsubscribe = void 0, (t = this.instance) == null || t.stop(), this.instance = void 0;
100
+ }
101
+ onTransition(e, t) {
102
+ (t == null ? void 0 : t.type) === "DOWNLOAD_SUCCESS" ? this.dispatchEvent(
103
+ new CustomEvent("llumi-download", {
104
+ detail: { filename: t.filename },
105
+ bubbles: !0,
106
+ composed: !0
107
+ })
108
+ ) : (t == null ? void 0 : t.type) === "DOWNLOAD_ERROR" && this.dispatchEvent(
109
+ new CustomEvent("llumi-download-error", {
110
+ detail: { error: t.error },
111
+ bubbles: !0,
112
+ composed: !0
113
+ })
114
+ ), this.requestUpdate();
115
+ }
116
+ onClick() {
117
+ var e, t;
118
+ if (!this.filename) {
119
+ console.warn(
120
+ '<llumi-download-button>: "filename" is required; ignoring click'
121
+ );
122
+ return;
123
+ }
124
+ (t = this.instance) == null || t.send({
125
+ type: "DOWNLOAD",
126
+ text: ((e = this.getValue) == null ? void 0 : e.call(this)) ?? "",
127
+ filename: this.filename,
128
+ mimeType: this.mimeType
129
+ });
130
+ }
131
+ viewFor(e) {
132
+ switch (e) {
133
+ case "idle":
134
+ return { icon: a.download, cls: "" };
135
+ case "downloading":
136
+ return { icon: a.spinner, cls: "is-working" };
137
+ case "downloaded":
138
+ return { icon: a.check, cls: "is-done" };
139
+ case "error":
140
+ return { icon: a.x, cls: "is-error" };
141
+ default:
142
+ return y(e);
143
+ }
144
+ }
145
+ render() {
146
+ var i;
147
+ const e = ((i = this.instance) == null ? void 0 : i.state.name) ?? "idle", t = this.viewFor(e);
148
+ return u`<button
149
+ type="button"
150
+ class=${t.cls}
151
+ title=${this.title}
152
+ aria-label=${this.title}
153
+ ?disabled=${e === "downloading"}
154
+ @click=${this.onClick}
155
+ >
156
+ <i>${t.icon}</i>
157
+ </button>`;
158
+ }
159
+ };
160
+ s([
161
+ l()
162
+ ], r.prototype, "filename", 2);
163
+ s([
164
+ l({ attribute: "mime-type" })
165
+ ], r.prototype, "mimeType", 2);
166
+ s([
167
+ l()
168
+ ], r.prototype, "title", 2);
169
+ s([
170
+ l({ attribute: !1 })
171
+ ], r.prototype, "getValue", 2);
172
+ r = s([
173
+ p("llumi-download-button")
174
+ ], r);
175
+ export {
176
+ r as LlumiDownloadButton
177
+ };
@@ -0,0 +1,45 @@
1
+ # llumi-highlighter
2
+
3
+ Syntax-highlighted code block (Prism via `refractor`), with 4 themes, an optional
4
+ copy button, and line-range dimming. Renders in **shadow DOM** (theme/token CSS is
5
+ encapsulated).
6
+
7
+ ## Usage
8
+
9
+ Load the design-system base stylesheet once (for the document), then the component:
10
+
11
+ ```html
12
+ <link rel="stylesheet" href="@llumi/design-system/base.css" />
13
+ <script type="module" src="@llumi/design-system/highlighter"></script>
14
+
15
+ <!-- code via the `code` property (recommended) -->
16
+ <llumi-highlighter language="typescript"></llumi-highlighter>
17
+ <script>
18
+ document.querySelector("llumi-highlighter").code =
19
+ "const greet = (name) => console.log(name);";
20
+ </script>
21
+
22
+ <!-- or as text content -->
23
+ <llumi-highlighter language="rust">fn main() { println!("hi"); }</llumi-highlighter>
24
+ ```
25
+
26
+ ## Properties / attributes
27
+
28
+ | Name | Attr | Type | Default | Description |
29
+ |------|------|------|---------|-------------|
30
+ | `language` | `language` | `string` | `""` | Prism language id (e.g. `typescript`, `python`). Aliases: `ts`, `js`, `py`, `rs`, `sh`, `md`, `html`. Unsupported → plain text + a console warning. |
31
+ | `theme` | `theme` | `ghcolors \| oneLight \| oneDark \| dracula` | `ghcolors` | Color theme. |
32
+ | `code` | `code` | `string` | `""` | Code source; falls back to the element's text content. |
33
+ | `highlight` | — (property) | `[number, number][]` | — | 1-based line ranges. When set, line numbers show and other lines dim to 30% opacity. |
34
+ | `copyButton` | `copy-button` | `boolean` | `true` | Show the copy button. Disable with `copy-button="false"`. |
35
+
36
+ ## Events
37
+
38
+ The embedded copy button emits (bubbling, composed — they cross the shadow boundary):
39
+ `llumi-copy` `{ detail: { text } }` and `llumi-copy-error` `{ detail: { error } }`.
40
+
41
+ ## Supported languages
42
+
43
+ `markup`/`html`/`xml`, `css`, `javascript`/`js`, `jsx`, `typescript`/`ts`, `tsx`,
44
+ `json`, `bash`/`sh`/`shell`, `python`/`py`, `rust`/`rs`, `go`, `java`, `yaml`,
45
+ `markdown`/`md`, `sql`, `diff`. (Grammars load lazily on first use.)