@imfusion/web-ui 0.6.4-dev.8.g41d03f2f → 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/CHANGELOG.md +391 -0
- package/README.md +22 -55
- package/dist/assets/vendors/base-ui.d.ts +2 -3
- package/dist/breakpoints/min-width.d.ts +3 -7
- package/dist/breakpoints/registry.d.ts +3 -9
- package/dist/{code-Ce8uiPsv.js → code-D2lwxHgd.js} +15 -15
- package/dist/codegen/gen-breakpoints-css.d.ts +0 -4
- package/dist/codegen/gen-token-css.d.ts +0 -2
- package/dist/components/app-shell/app-shell.d.ts +7 -11
- package/dist/components/button/button.d.ts +7 -12
- package/dist/components/callout/callout.d.ts +11 -18
- package/dist/components/card/card.d.ts +11 -10
- package/dist/components/checkbox/checkbox.d.ts +2 -38
- package/dist/components/chip/chip.cva.d.ts +2 -4
- package/dist/components/chip/chip.d.ts +1 -1
- package/dist/components/code/code.d.ts +2 -2
- package/dist/components/collapsible/collapsible.d.ts +4 -35
- package/dist/components/copy-button/copy-button.d.ts +1 -1
- package/dist/components/drawer/drawer.d.ts +17 -100
- package/dist/components/field/field.d.ts +7 -67
- package/dist/components/fieldset/fieldset.d.ts +2 -15
- package/dist/components/icon/icon.d.ts +2 -2
- package/dist/components/input/input.d.ts +2 -8
- package/dist/components/logo/imfusion/imfusion.d.ts +2 -5
- package/dist/components/logo/imfusion/marks.d.ts +10 -0
- package/dist/components/logo/logo.d.ts +3 -3
- package/dist/components/navigation-menu/subs/flyout-link.d.ts +5 -19
- package/dist/components/navigation-menu/subs/inline-submenu.d.ts +2 -4
- package/dist/components/navigation-menu/subs/link.d.ts +6 -23
- package/dist/components/navigation-menu/subs/overlay.d.ts +6 -51
- package/dist/components/navigation-menu/subs/shared.d.ts +4 -14
- package/dist/components/navigation-menu/subs/structure.d.ts +7 -29
- package/dist/components/navigation-menu/subs/trigger.d.ts +11 -20
- package/dist/components/number-field/index.d.ts +2 -0
- package/dist/components/number-field/number-field.d.ts +72 -0
- package/dist/components/number-field/number-field.meta.d.ts +2 -0
- package/dist/components/popover/popover.d.ts +23 -98
- package/dist/components/row/row.d.ts +1 -1
- package/dist/components/select/select.d.ts +26 -189
- package/dist/components/separator/separator.d.ts +5 -4
- package/dist/components/slider/slider.d.ts +7 -77
- package/dist/components/spinner/spinner.d.ts +13 -8
- package/dist/components/stack/stack.d.ts +3 -4
- package/dist/components/switch/switch.d.ts +2 -31
- package/dist/components/table/table.d.ts +10 -18
- package/dist/components/tabs/tabs.d.ts +18 -52
- package/dist/components/toast/toast.d.ts +16 -103
- package/dist/components/toggle/toggle.d.ts +3 -21
- package/dist/components/toggle-group/toggle-group.d.ts +3 -20
- package/dist/components/tooltip/tooltip.d.ts +26 -85
- package/dist/components/typo/typo.d.ts +25 -25
- package/dist/docgen/gen-docgen.utils.d.ts +3 -4
- package/dist/hooks/use-color-scheme.d.ts +5 -11
- package/dist/hooks/use-media-query.d.ts +2 -7
- package/dist/{icons-Cy1HAosO.js → icons-CaGCCwG-.js} +7 -7
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1486 -1368
- package/dist/integrations/code-highlight/highlighter.d.ts +1 -56
- package/dist/integrations/code-highlight.js +41 -189
- package/dist/integrations/image-display-options/image-display-options-view.utils.d.ts +0 -1
- package/dist/integrations/image-display-options.js +54 -54
- package/dist/llms/evals/runner/affected.d.ts +2 -0
- package/dist/llms/evals/runner/benchmark.d.ts +12 -0
- package/dist/llms/evals/runner/compare.d.ts +2 -0
- package/dist/llms/evals/runner/config.d.ts +29 -0
- package/dist/llms/evals/runner/evidence.d.ts +7 -0
- package/dist/llms/evals/runner/execute.d.ts +32 -0
- package/dist/llms/evals/runner/grade.d.ts +10 -0
- package/dist/llms/evals/runner/report-md.d.ts +2 -0
- package/dist/llms/evals/runner/report.d.ts +4 -0
- package/dist/llms/evals/runner/run.d.ts +2 -0
- package/dist/llms/evals/runner/scenario.d.ts +3 -0
- package/dist/llms/evals/runner/types.d.ts +140 -0
- package/dist/llms/evals/runner/workspace.d.ts +2 -0
- package/dist/llms/evals/runner/write-generated.d.ts +1 -0
- package/dist/provider/web-ui-provider.d.ts +4 -1
- package/dist/style.css +1 -1
- package/dist/tabs-BS6rqrrA.js +398 -0
- package/dist/tokens/apply.d.ts +4 -9
- package/dist/tokens/control-registry.d.ts +3 -6
- package/dist/tokens/types.d.ts +7 -24
- package/dist/tokens/use-token-controls.d.ts +3 -12
- package/dist/types/meta.d.ts +15 -32
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/resolve-state-props.d.ts +4 -0
- package/dist/vite/brand-assets.d.ts +3 -0
- package/dist/vite/index.d.ts +2 -0
- package/dist/vite/readable-css-module-names.d.ts +11 -0
- package/dist/vite.js +55 -0
- package/dist/web-ui-cli.js +322 -0
- package/docs/user-guide/AgentTooling.mdx +133 -0
- package/docs/user-guide/BrandAssets.mdx +103 -0
- package/docs/user-guide/Changelog.mdx +26 -0
- package/docs/user-guide/GettingStarted.mdx +71 -0
- package/docs/user-guide/HowItsBuilt.mdx +114 -0
- package/docs/user-guide/Tokens.mdx +88 -0
- package/docs/user-guide/UsagePatterns.mdx +123 -0
- package/package.json +28 -22
- package/src/assets/public/favicon/apple-touch-icon.png +0 -0
- package/src/assets/public/favicon/favicon-16.png +0 -0
- package/src/assets/public/favicon/favicon-32.png +0 -0
- package/src/assets/public/favicon/favicon.ico +0 -0
- package/src/assets/public/favicon/favicon.svg +7 -0
- package/src/assets/public/favicon/icon-192.png +0 -0
- package/src/assets/public/favicon/icon-512.png +0 -0
- package/src/assets/public/favicon/og-image.png +0 -0
- package/src/assets/public/spinner/imfusion-spinner-black.webp +0 -0
- package/src/assets/public/spinner/imfusion-spinner-blue.webp +0 -0
- package/src/assets/public/spinner/imfusion-spinner-white.webp +0 -0
- package/src/docgen/doc.gen.json +3255 -603
- package/src/llms/install-templates/AGENTS.md +4 -4
- package/src/llms/install-templates/hooks/baseline-staleness.sh +9 -2
- package/src/llms/llms.gen.txt +7 -1
- package/src/llms/skills/imf-web-ui/SKILL.md +44 -21
- package/src/llms/skills/imf-web-ui-audit/SKILL.md +25 -11
- package/src/llms/skills/imf-web-ui-components/SKILL.md +7 -3
- package/src/llms/skills/imf-web-ui-conventions/SKILL.md +3 -2
- package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +0 -1
- package/src/llms/skills/imf-web-ui-conventions/templates/REPORT.md +21 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/agent-tooling.md +15 -10
- package/src/llms/skills/imf-web-ui-conventions/topics/assets.md +7 -4
- package/src/llms/skills/imf-web-ui-conventions/topics/data.md +3 -1
- package/src/llms/skills/imf-web-ui-conventions/topics/docs-structure.md +2 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/git.md +4 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/library-setup.md +10 -1
- package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +2 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/tokens.md +4 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +13 -11
- package/src/llms/skills/imf-web-ui-setup/SKILL.md +17 -9
- package/src/llms/skills/imf-web-ui-update/SKILL.md +47 -19
- package/src/llms/skills/imf-web-ui-ux/SKILL.md +6 -3
- package/src/llms/tokens.gen.json +79 -15
- package/bin/install.js +0 -446
- package/dist/build/vite-css-module-names/index.d.ts +0 -20
- package/dist/build/vite-css-module-names.js +0 -17
- package/dist/components/spinner/spinner.geometry.d.ts +0 -37
- package/dist/integrations/code-highlight/languages/cmake.d.ts +0 -1
- package/dist/integrations/code-highlight/languages/cpp.d.ts +0 -1
- package/dist/tabs-DIe1Utiy.js +0 -371
- /package/dist/{meta-CySnRuVp.js → meta-j-7HGrWv.js} +0 -0
package/dist/vite.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readFileSync as e, readdirSync as t } from "node:fs";
|
|
2
|
+
import { dirname as n, join as r } from "node:path";
|
|
3
|
+
import { fileURLToPath as i } from "node:url";
|
|
4
|
+
//#region src/vite/brand-assets.ts
|
|
5
|
+
var a = n(i(import.meta.resolve("@imfusion/web-ui/assets/favicon/favicon.svg"))), o = t(a).map((t) => ({
|
|
6
|
+
name: t,
|
|
7
|
+
source: e(r(a, t))
|
|
8
|
+
})), s = {
|
|
9
|
+
".ico": "image/x-icon",
|
|
10
|
+
".png": "image/png",
|
|
11
|
+
".svg": "image/svg+xml"
|
|
12
|
+
};
|
|
13
|
+
function c(e) {
|
|
14
|
+
return s[e.slice(e.lastIndexOf("."))] ?? "application/octet-stream";
|
|
15
|
+
}
|
|
16
|
+
function l() {
|
|
17
|
+
return {
|
|
18
|
+
name: "imf-ui:brand-assets",
|
|
19
|
+
configureServer(e) {
|
|
20
|
+
e.middlewares.use((e, t, n) => {
|
|
21
|
+
let r = o.find(({ name: t }) => e.url?.split("?")[0] === `/${t}`);
|
|
22
|
+
if (!r) {
|
|
23
|
+
n();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
t.setHeader("Content-Type", c(r.name)), t.end(r.source);
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
generateBundle() {
|
|
30
|
+
for (let e of o) this.emitFile({
|
|
31
|
+
fileName: e.name,
|
|
32
|
+
source: e.source,
|
|
33
|
+
type: "asset"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/vite/readable-css-module-names.ts
|
|
40
|
+
function u(e, t) {
|
|
41
|
+
let { lightningcss: n, modules: r, transformer: i } = e.css ?? {}, a = n?.cssModules;
|
|
42
|
+
if (!(typeof a == "boolean" || r === !1) && !a?.pattern && !(typeof r == "object" && r.generateScopedName)) return i === "postcss" ? { css: { modules: { generateScopedName: t } } } : { css: {
|
|
43
|
+
transformer: "lightningcss",
|
|
44
|
+
lightningcss: { cssModules: { pattern: t } }
|
|
45
|
+
} };
|
|
46
|
+
}
|
|
47
|
+
function d({ prefix: e }) {
|
|
48
|
+
let t = `${e}-[name]-[local]`;
|
|
49
|
+
return {
|
|
50
|
+
name: "imf-ui:readable-css-module-names",
|
|
51
|
+
config: (e) => u(e, t)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { l as imfusionBrandAssets, d as readableCssModuleNames };
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { chmodSync as e, cpSync as t, existsSync as n, lstatSync as r, mkdirSync as i, readFileSync as a, readdirSync as o, readlinkSync as s, realpathSync as c, rmSync as l, symlinkSync as u, writeFileSync as d } from "node:fs";
|
|
3
|
+
import { dirname as f, relative as p, resolve as m } from "node:path";
|
|
4
|
+
import { fileURLToPath as ee } from "node:url";
|
|
5
|
+
import { Command as te, Option as ne } from "commander";
|
|
6
|
+
import * as h from "@clack/prompts";
|
|
7
|
+
import { spawnSync as g } from "node:child_process";
|
|
8
|
+
var _ = {
|
|
9
|
+
harnesses: {
|
|
10
|
+
claude: {
|
|
11
|
+
label: "Claude Code",
|
|
12
|
+
hookSettings: ".claude/settings.json",
|
|
13
|
+
hookTemplate: "settings.json"
|
|
14
|
+
},
|
|
15
|
+
codex: {
|
|
16
|
+
label: "Codex",
|
|
17
|
+
hookSettings: ".codex/hooks.json",
|
|
18
|
+
hookTemplate: "codex-hooks.json"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
namespace: "imf-web-ui",
|
|
22
|
+
hooksDir: ".agents/hooks/imf-web-ui",
|
|
23
|
+
templatesDir: "src/llms/install-templates",
|
|
24
|
+
skillsSource: "src/llms/skills",
|
|
25
|
+
skillsDir: ".agents/skills",
|
|
26
|
+
linkDir: ".claude/skills",
|
|
27
|
+
versionMarker: ".imf-web-ui-skill-version.json"
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/cli/paths.ts
|
|
31
|
+
function re(e) {
|
|
32
|
+
let t = e;
|
|
33
|
+
for (; !n(m(t, "package.json"));) {
|
|
34
|
+
let n = f(t);
|
|
35
|
+
if (n === t) throw Error(`Could not find package.json above ${e}`);
|
|
36
|
+
t = n;
|
|
37
|
+
}
|
|
38
|
+
return t;
|
|
39
|
+
}
|
|
40
|
+
var v = re(f(ee(import.meta.url))), y = process.cwd(), ie = `<!-- ${_.namespace}:begin`, b = `<!-- ${_.namespace}:end -->`, ae = m(v, _.templatesDir, "AGENTS.md"), x = m(y, "AGENTS.md");
|
|
41
|
+
function S(e) {
|
|
42
|
+
let t = e.indexOf(ie), n = e.indexOf(b);
|
|
43
|
+
return t === -1 || n === -1 ? null : e.slice(t, n + b.length);
|
|
44
|
+
}
|
|
45
|
+
function oe() {
|
|
46
|
+
if (!n(x)) return "absent";
|
|
47
|
+
let e = S(a(ae, "utf-8"));
|
|
48
|
+
if (!e) return "absent";
|
|
49
|
+
let t = a(x, "utf-8"), r = S(t), i = r ? t.replace(r, e) : `${t.trimEnd()}\n\n${e}\n`;
|
|
50
|
+
return i === t ? "unchanged" : (d(x, i), r ? "updated" : "added");
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/cli/skills.ts
|
|
54
|
+
var C = Object.keys(_.harnesses).filter((e) => e in _.harnesses), se = [...C, "all"];
|
|
55
|
+
function w(e) {
|
|
56
|
+
return e === "all" ? C : [e];
|
|
57
|
+
}
|
|
58
|
+
function T(e) {
|
|
59
|
+
return `./${p(y, e)}`;
|
|
60
|
+
}
|
|
61
|
+
var E = _.namespace, D = _.versionMarker, O = m(v, _.skillsSource), k = m(y, _.skillsDir), A = m(y, _.linkDir), j = class extends Error {}, M = JSON.parse(a(m(v, "package.json"), "utf-8"));
|
|
62
|
+
if (typeof M != "object" || !M || !("version" in M) || typeof M.version != "string") throw Error(`${m(v, "package.json")} has no string "version" field`);
|
|
63
|
+
var N = M.version;
|
|
64
|
+
function P(e) {
|
|
65
|
+
return e === E || e.startsWith(`${E}-`);
|
|
66
|
+
}
|
|
67
|
+
function F() {
|
|
68
|
+
return n(O) ? o(O, { withFileTypes: !0 }).filter((e) => e.isDirectory() && P(e.name)).map((e) => e.name).sort() : [];
|
|
69
|
+
}
|
|
70
|
+
function I(e) {
|
|
71
|
+
let t = m(k, e, D);
|
|
72
|
+
if (!n(t)) return null;
|
|
73
|
+
try {
|
|
74
|
+
let e = JSON.parse(a(t, "utf-8"));
|
|
75
|
+
if (typeof e != "object" || !e) return null;
|
|
76
|
+
let n = "harnesses" in e && Array.isArray(e.harnesses) ? e.harnesses : null;
|
|
77
|
+
return n ? C.filter((e) => n.includes(e)) : null;
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function L(e) {
|
|
83
|
+
for (let t of e) {
|
|
84
|
+
let e = I(t);
|
|
85
|
+
if (e && e.length > 0) return e;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
function R(e, t, r) {
|
|
90
|
+
if (n(e)) for (let n of o(e).filter(P)) t.has(n) || (l(m(e, n), {
|
|
91
|
+
recursive: !0,
|
|
92
|
+
force: !0
|
|
93
|
+
}), h.log.info(`Removed skill: ${n} (${r})`));
|
|
94
|
+
}
|
|
95
|
+
function ce() {
|
|
96
|
+
return n(A) && n(k) && c(A) === c(k);
|
|
97
|
+
}
|
|
98
|
+
function le(e) {
|
|
99
|
+
let t = m(A, e), n = p(A, m(k, e)), a = r(t, { throwIfNoEntry: !1 });
|
|
100
|
+
a?.isSymbolicLink() && s(t) === n || (a && l(t, {
|
|
101
|
+
recursive: !0,
|
|
102
|
+
force: !0
|
|
103
|
+
}), i(A, { recursive: !0 }), u(n, t));
|
|
104
|
+
}
|
|
105
|
+
function z(e, n) {
|
|
106
|
+
try {
|
|
107
|
+
i(k, { recursive: !0 });
|
|
108
|
+
for (let r of e) {
|
|
109
|
+
let e = m(k, r);
|
|
110
|
+
l(e, {
|
|
111
|
+
recursive: !0,
|
|
112
|
+
force: !0
|
|
113
|
+
}), t(m(O, r), e, { recursive: !0 }), d(m(e, D), JSON.stringify({
|
|
114
|
+
version: N,
|
|
115
|
+
harnesses: n
|
|
116
|
+
}, null, 2) + "\n");
|
|
117
|
+
}
|
|
118
|
+
if (R(k, new Set(e), "no longer in this bundle"), h.log.success(`Skills -> ${T(k)}/${E}*`), ce()) return;
|
|
119
|
+
if (n.includes("claude")) {
|
|
120
|
+
for (let t of e) le(t);
|
|
121
|
+
R(A, new Set(e), "no longer in this bundle"), h.log.success(`Claude Code -> ${T(A)}/${E}* (symlinks -> ${T(k)})`);
|
|
122
|
+
} else R(A, /* @__PURE__ */ new Set(), "claude not selected");
|
|
123
|
+
} catch (e) {
|
|
124
|
+
throw e instanceof Error ? new j(e.message) : e;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/cli/hooks.ts
|
|
129
|
+
var B = m(v, _.templatesDir), V = m(B, "hooks"), H = m(y, _.hooksDir), U = class extends Error {};
|
|
130
|
+
function ue(e, t) {
|
|
131
|
+
if (typeof e != "object" || !e || Array.isArray(e)) throw new U(`${t} is not a JSON object.`);
|
|
132
|
+
let n = { ...e };
|
|
133
|
+
if ("hooks" in n) {
|
|
134
|
+
if (typeof n.hooks != "object" || n.hooks === null || Array.isArray(n.hooks)) throw new U(`${t}: "hooks" is not a JSON object.`);
|
|
135
|
+
for (let [e, r] of Object.entries(n.hooks)) if (!Array.isArray(r)) throw new U(`${t}: "hooks.${e}" is not a JSON array.`);
|
|
136
|
+
}
|
|
137
|
+
return n;
|
|
138
|
+
}
|
|
139
|
+
function W(e) {
|
|
140
|
+
let t;
|
|
141
|
+
try {
|
|
142
|
+
t = JSON.parse(a(e, "utf-8"));
|
|
143
|
+
} catch {
|
|
144
|
+
throw new U(`${e} is not valid JSON.`);
|
|
145
|
+
}
|
|
146
|
+
return ue(t, e);
|
|
147
|
+
}
|
|
148
|
+
function de(e) {
|
|
149
|
+
let t = y;
|
|
150
|
+
for (; t !== f(t) && !n(m(t, "node_modules", ".bin"));) t = f(t);
|
|
151
|
+
let r = m(t, "node_modules", ".bin", "prettier");
|
|
152
|
+
n(r) && g(r, ["--write", e], {
|
|
153
|
+
cwd: y,
|
|
154
|
+
stdio: "ignore"
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function fe() {
|
|
158
|
+
let n = o(V).filter((e) => e.endsWith(".sh"));
|
|
159
|
+
i(H, { recursive: !0 });
|
|
160
|
+
for (let r of n) {
|
|
161
|
+
let n = m(H, r);
|
|
162
|
+
t(m(V, r), n, { force: !0 }), e(n, 493);
|
|
163
|
+
}
|
|
164
|
+
let r = o(H).filter((e) => e.endsWith(".sh") && !n.includes(e));
|
|
165
|
+
for (let e of r) l(m(H, e), { force: !0 });
|
|
166
|
+
return {
|
|
167
|
+
installed: n,
|
|
168
|
+
removed: r
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
var G = `${_.hooksDir}/`;
|
|
172
|
+
function K(e) {
|
|
173
|
+
if (typeof e != "string") return null;
|
|
174
|
+
let t = e.lastIndexOf(G);
|
|
175
|
+
if (t === -1) return null;
|
|
176
|
+
let n = /^([\w.-]+)["']?$/.exec(e.slice(t + G.length));
|
|
177
|
+
return n ? n[1] : null;
|
|
178
|
+
}
|
|
179
|
+
function q(e) {
|
|
180
|
+
return typeof e == "object" && !!e && "hooks" in e && Array.isArray(e.hooks);
|
|
181
|
+
}
|
|
182
|
+
function J(e) {
|
|
183
|
+
return q(e) ? e.hooks : null;
|
|
184
|
+
}
|
|
185
|
+
function Y(e) {
|
|
186
|
+
return typeof e == "object" && e && "command" in e ? e.command : void 0;
|
|
187
|
+
}
|
|
188
|
+
function X(e) {
|
|
189
|
+
let t = J(e);
|
|
190
|
+
return t ? t.map((e) => K(Y(e))).filter((e) => e !== null) : [];
|
|
191
|
+
}
|
|
192
|
+
function pe(e, t, n) {
|
|
193
|
+
e.hooks ??= {};
|
|
194
|
+
let r = 0, i = 0, a = [];
|
|
195
|
+
for (let [r, o] of Object.entries(e.hooks)) {
|
|
196
|
+
let s = 0, c = o.map((e) => {
|
|
197
|
+
if (!q(e)) return e;
|
|
198
|
+
let t = e.hooks, r = t.filter((e) => {
|
|
199
|
+
let t = K(Y(e));
|
|
200
|
+
return t === null || n.includes(t);
|
|
201
|
+
});
|
|
202
|
+
return s += t.length - r.length, r.length === t.length ? e : {
|
|
203
|
+
...e,
|
|
204
|
+
hooks: r
|
|
205
|
+
};
|
|
206
|
+
}).filter((e) => {
|
|
207
|
+
let t = J(e);
|
|
208
|
+
return t === null || t.length > 0;
|
|
209
|
+
});
|
|
210
|
+
s !== 0 && (i += s, c.length === 0 && !(r in t) ? a.push(r) : e.hooks[r] = c);
|
|
211
|
+
}
|
|
212
|
+
a.length > 0 && (e.hooks = Object.fromEntries(Object.entries(e.hooks).filter(([e]) => !a.includes(e))));
|
|
213
|
+
for (let [n, i] of Object.entries(t)) {
|
|
214
|
+
e.hooks[n] ??= [];
|
|
215
|
+
for (let t of i) {
|
|
216
|
+
let i = X(t);
|
|
217
|
+
e.hooks[n].some((e) => X(e).some((e) => i.includes(e))) || (e.hooks[n].push(t), r++);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
added: r,
|
|
222
|
+
removed: i
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function me(e) {
|
|
226
|
+
let t = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map();
|
|
227
|
+
for (let i of C) {
|
|
228
|
+
let a = m(y, _.harnesses[i].hookSettings);
|
|
229
|
+
if (n(a) && t.set(i, W(a)), e.includes(i)) {
|
|
230
|
+
let e = m(B, _.harnesses[i].hookTemplate);
|
|
231
|
+
r.set(i, W(e).hooks ?? {});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
let { installed: a, removed: o } = fe();
|
|
235
|
+
h.log.success(`Agent hooks -> ${T(H)} (${String(a.length)} scripts)`), o.length > 0 && h.log.info(`Removed stale hook script(s): ${o.join(", ")} (no longer shipped)`);
|
|
236
|
+
for (let n of C) {
|
|
237
|
+
let o = e.includes(n);
|
|
238
|
+
if (!o && !t.has(n)) continue;
|
|
239
|
+
let s = m(y, _.harnesses[n].hookSettings), c = r.get(n) ?? {}, l = t.get(n) ?? {}, u = pe(l, c, o ? a : []);
|
|
240
|
+
if (u.added === 0 && u.removed === 0) {
|
|
241
|
+
o && h.log.info(`Hook registrations already present in ${T(s)}`);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
i(f(s), { recursive: !0 }), d(s, JSON.stringify(l, null, 2) + "\n"), de(s);
|
|
245
|
+
let p = [u.added > 0 && `registered ${String(u.added)}`, u.removed > 0 && `removed ${String(u.removed)} stale`].filter(Boolean);
|
|
246
|
+
h.log.success(`${p.join(", ")} hook registration(s) in ${T(s)}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//#endregion
|
|
250
|
+
//#region src/cli/index.ts
|
|
251
|
+
function Z() {
|
|
252
|
+
return new ne("--harness <harness>", "claude, codex, or all").choices(se);
|
|
253
|
+
}
|
|
254
|
+
async function he(e, t, n) {
|
|
255
|
+
if (t) return h.log.info(`Harness from --harness flag: ${t.join(", ")}`), t;
|
|
256
|
+
let r = L(e);
|
|
257
|
+
if (r && !n) return h.log.info(`Refreshing the existing install: ${r.join(", ")} — pass --reconfigure to choose differently.`), r;
|
|
258
|
+
if (!process.stdin.isTTY) return console.error("Pass --harness=claude|codex|all — no terminal to prompt in."), process.exitCode = 1, null;
|
|
259
|
+
let i = await h.multiselect({
|
|
260
|
+
message: "Install into which harness (or harnesses)?",
|
|
261
|
+
options: C.map((e) => ({
|
|
262
|
+
value: e,
|
|
263
|
+
label: _.harnesses[e].label
|
|
264
|
+
})),
|
|
265
|
+
required: !0
|
|
266
|
+
});
|
|
267
|
+
return h.isCancel(i) ? (h.cancel("Cancelled — nothing installed."), null) : i;
|
|
268
|
+
}
|
|
269
|
+
async function ge(e) {
|
|
270
|
+
let t = e.harness ? w(e.harness) : null, n = F();
|
|
271
|
+
if (h.intro(`@imfusion/web-ui install skills — ${String(n.length)} skills`), n.length === 0) {
|
|
272
|
+
h.log.error("No skills found. Reinstall @imfusion/web-ui and try again."), h.outro("Nothing installed."), process.exitCode = 1;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
let r = await he(n, t, e.reconfigure === !0);
|
|
276
|
+
if (!r) return;
|
|
277
|
+
h.log.message(`Skills in this bundle:\n${n.map((e) => ` - ${e}`).join("\n")}`);
|
|
278
|
+
try {
|
|
279
|
+
z(n, r);
|
|
280
|
+
} catch (e) {
|
|
281
|
+
if (e instanceof j) {
|
|
282
|
+
h.log.error(e.message), h.outro("Install failed."), process.exitCode = 1;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
throw e;
|
|
286
|
+
}
|
|
287
|
+
let i = oe();
|
|
288
|
+
(i === "updated" || i === "added") && h.log.success(`Refreshed the ${_.namespace} block in ./AGENTS.md`), h.outro(`Done — ${String(n.length)} skills installed.`);
|
|
289
|
+
}
|
|
290
|
+
function _e(e) {
|
|
291
|
+
let t = e.harness ? w(e.harness) : null, n = L(F());
|
|
292
|
+
if (!t && !n) {
|
|
293
|
+
console.error("No harness recorded. Pass --harness=claude|codex|all."), process.exitCode = 1;
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
let r = t ?? n ?? [], i = r.filter((e) => !(n ?? []).includes(e));
|
|
297
|
+
if (i.length > 0) {
|
|
298
|
+
for (let e of i) console.error(`No skills installed for ${e}. Run \`npx web-ui install skills --harness=${e}\` first.`);
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
h.intro("@imfusion/web-ui install hooks");
|
|
303
|
+
try {
|
|
304
|
+
me(r);
|
|
305
|
+
} catch (e) {
|
|
306
|
+
if (e instanceof U) {
|
|
307
|
+
console.error(e.message), process.exitCode = 1;
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
throw e;
|
|
311
|
+
}
|
|
312
|
+
h.outro("Done — agent hooks installed.");
|
|
313
|
+
}
|
|
314
|
+
var Q = new te();
|
|
315
|
+
Q.name("web-ui").description("Command-line tools for @imfusion/web-ui").version(N);
|
|
316
|
+
var $ = Q.command("install").description("Install @imfusion/web-ui agent skills or hooks into a project");
|
|
317
|
+
$.command("skills").description("Install skills for the chosen harness").addOption(Z()).option("--reconfigure", "Re-open the harness selection prompt on an existing install").action(ge), $.command("hooks").description("Install agent lifecycle hooks (requires skills already installed for the harness)").addOption(Z()).action(_e), $.action(() => {
|
|
318
|
+
$.help();
|
|
319
|
+
}), Q.action(() => {
|
|
320
|
+
Q.help();
|
|
321
|
+
}), await Q.parseAsync();
|
|
322
|
+
//#endregion
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
import { Typo } from "#/components/typo";
|
|
3
|
+
|
|
4
|
+
<Meta title="User Guide/Agent Tooling" />
|
|
5
|
+
|
|
6
|
+
<Typo.Hero>Agent tooling</Typo.Hero>
|
|
7
|
+
|
|
8
|
+
The package ships skills and lifecycle hooks for coding agents working in a project that uses `@imfusion/web-ui`. Both are
|
|
9
|
+
optional, but highly recommended: the library is built to be set up and used through an agent.
|
|
10
|
+
|
|
11
|
+
## Supported agents
|
|
12
|
+
|
|
13
|
+
The skills follow the [Agent Skills](https://agentskills.io/) standard and install into `.agents/skills/`. Project
|
|
14
|
+
instructions go into [`AGENTS.md`](https://agents.md/): when the project has one, the installer keeps a fenced Web UI block
|
|
15
|
+
in it up to date.
|
|
16
|
+
|
|
17
|
+
| Agent | Skills | `AGENTS.md` | Hooks |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| [Codex](https://developers.openai.com/codex/skills) | Reads `.agents/skills/` | [Reads it](https://developers.openai.com/codex/guides/agents-md) | [Supported](https://developers.openai.com/codex/hooks) |
|
|
20
|
+
| [Claude Code](https://code.claude.com/docs/en/skills) | Reads only `.claude/skills/`, so the installer links each skill there | [Reads it](https://code.claude.com/docs/en/changelog#2-1-277) when the project has no `CLAUDE.md`; otherwise add `@AGENTS.md` to `CLAUDE.md` | [Supported](https://code.claude.com/docs/en/hooks) |
|
|
21
|
+
| Other Agent Skills clients | Check the agent's docs for `.agents/skills/` | Check the agent's docs | Not installed |
|
|
22
|
+
|
|
23
|
+
The installer targets Codex and Claude Code. Other agents that implement the standard are listed on
|
|
24
|
+
[agentskills.io](https://agentskills.io/); one that reads `.agents/skills/` picks up the same skills, without the hooks.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
Install the package first, then run this in the project root:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npx web-ui install skills --harness=all
|
|
32
|
+
npx web-ui install hooks --harness=all
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`--harness` picks the agent the tooling is installed for:
|
|
36
|
+
|
|
37
|
+
| Value | Installs for |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `claude` | Claude Code |
|
|
40
|
+
| `codex` | Codex, and any agent that reads `.agents/skills/` |
|
|
41
|
+
| `all` | Both |
|
|
42
|
+
|
|
43
|
+
Without `--harness`, `install skills` prompts once and records the choice, and later runs reuse it. `install hooks` needs the
|
|
44
|
+
skills installed for the same harness first. Leave out the second command to install only the skills.
|
|
45
|
+
|
|
46
|
+
`npx web-ui --help` lists the commands, and `--help` on any subcommand lists its options.
|
|
47
|
+
|
|
48
|
+
## Skills
|
|
49
|
+
|
|
50
|
+
`/imf-web-ui` is the router: the entry point for UI work and library questions. It routes usage questions to the packaged user
|
|
51
|
+
guides and development tasks to the matching companion skill, and it keeps small tasks small instead of loading every
|
|
52
|
+
reference just because the package is installed.
|
|
53
|
+
|
|
54
|
+
With the hooks installed, the agent is pointed at the router at the start of every session. Without them, the agent usually
|
|
55
|
+
picks it up from the skill's description; invoke it by name when it does not.
|
|
56
|
+
|
|
57
|
+
The router answers library questions from the packaged user guides and hands other tasks to a skill. Setup, audit, and UX
|
|
58
|
+
work from `imf-web-ui-conventions`, a knowledge skill that holds the ImFusion frontend conventions. Solid outlines are the
|
|
59
|
+
skills you run; dashed boxes are knowledge the agent reads:
|
|
60
|
+
|
|
61
|
+
<ConsumerLlmFrameworkDiagram />
|
|
62
|
+
|
|
63
|
+
| Skill | Use it for |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `/imf-web-ui` | The router: any UI task or library question. |
|
|
66
|
+
| `/imf-web-ui-components` | Component props, parts, defaults, and icons. |
|
|
67
|
+
| `/imf-web-ui-ux` | Choosing components and shaping screens or flows. |
|
|
68
|
+
| `/imf-web-ui-setup` | First-time wiring and approved project setup. |
|
|
69
|
+
| `/imf-web-ui-audit` | A read-only check of an existing project. |
|
|
70
|
+
| `/imf-web-ui-update` | Updating the package and its installed tooling. |
|
|
71
|
+
|
|
72
|
+
## Hooks
|
|
73
|
+
|
|
74
|
+
The hooks put the agent on the right skill at the right moment and ask it to verify the project after it edits source files:
|
|
75
|
+
|
|
76
|
+
| Event | What the hook does |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `SessionStart` | Points the agent at the router once per session. |
|
|
79
|
+
| `SubagentStart` | Gives the same pointer to spawned agents. |
|
|
80
|
+
| `UserPromptSubmit` | Names the companion skills relevant to the prompt. |
|
|
81
|
+
| `Stop` | Asks for project verification after a turn edits source files. |
|
|
82
|
+
|
|
83
|
+
## Keeping it current
|
|
84
|
+
|
|
85
|
+
Ask the agent to update the library, for example "Update @imfusion/web-ui". That runs `/imf-web-ui-update`, which installs
|
|
86
|
+
the newer package and refreshes the skills, hook scripts, and registrations to match it.
|
|
87
|
+
|
|
88
|
+
To do the same by hand, update the package, then run:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
npx web-ui install skills
|
|
92
|
+
npx web-ui install hooks
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Both reuse the recorded harness. Leave out the second command if the project skips hooks. Together they bring the installed
|
|
96
|
+
tooling in line with the package version and remove anything the package no longer ships.
|
|
97
|
+
|
|
98
|
+
## Reference
|
|
99
|
+
|
|
100
|
+
### Where the files go
|
|
101
|
+
|
|
102
|
+
- Skills: `.agents/skills/` for every harness. `claude` also links each skill from `.claude/skills/`.
|
|
103
|
+
- Hook scripts: `.agents/hooks/imf-web-ui/`.
|
|
104
|
+
- Hook registrations: `.claude/settings.json` for Claude Code, `.codex/hooks.json` for Codex.
|
|
105
|
+
|
|
106
|
+
### Changing the harness
|
|
107
|
+
|
|
108
|
+
Pass a different `--harness`, or `--reconfigure` to get the prompt again. Dropping `claude` removes its `.claude/skills`
|
|
109
|
+
links. The next `install hooks` run removes the registrations from a harness that is no longer selected and adds them for the
|
|
110
|
+
newly selected one.
|
|
111
|
+
|
|
112
|
+
### What the installer owns
|
|
113
|
+
|
|
114
|
+
The installer owns every skill directory named `imf-web-ui` or `imf-web-ui-*`. It overwrites and prunes those directories and
|
|
115
|
+
leaves every other skill alone, so a project must not put its own skills under that prefix.
|
|
116
|
+
|
|
117
|
+
Hook registrations merge into the chosen harness's file without touching unrelated entries. For each event, the installer
|
|
118
|
+
adds a shipped hook unless that same script is already registered there; other hooks on that event stay alongside it.
|
|
119
|
+
|
|
120
|
+
An agent reads the `agent-tooling` topic in the installed `imf-web-ui-conventions` skill before adapting a hook registration.
|
|
121
|
+
|
|
122
|
+
### Codex trust
|
|
123
|
+
|
|
124
|
+
Codex only runs project hooks after the project is trusted and each script is approved through `/hooks`. Revisit that review
|
|
125
|
+
after a hook script changes. Claude Code has no equivalent trust gate.
|
|
126
|
+
|
|
127
|
+
### Where the files come from
|
|
128
|
+
|
|
129
|
+
The skills ship inside the package under `src/llms/skills/`, and `web-ui install` copies them into the project. The package
|
|
130
|
+
also exposes generated component, token, and icon indexes for the lookup skill. User guides ship as readable MDX under
|
|
131
|
+
`docs/user-guide/`; agents read them from the installed package without a running Storybook.
|
|
132
|
+
|
|
133
|
+
<PageNav />
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
import faviconUrl from "#/assets/public/favicon/favicon.svg";
|
|
3
|
+
import ogUrl from "#/assets/public/favicon/og-image.png";
|
|
4
|
+
import { Typo } from "#/components/typo";
|
|
5
|
+
|
|
6
|
+
<Meta title="User Guide/Brand Assets" />
|
|
7
|
+
|
|
8
|
+
<Typo.Hero>Brand assets</Typo.Hero>
|
|
9
|
+
|
|
10
|
+
The package ships the ImFusion favicon set and a social sharing image as plain files. Import them through the `assets`
|
|
11
|
+
subpath:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
@imfusion/web-ui/assets/<folder>/<file>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
These are static files rather than components, so an application references them from its HTML head, copies them into its own
|
|
18
|
+
output, or resolves them on disk during its build.
|
|
19
|
+
|
|
20
|
+
## Files
|
|
21
|
+
|
|
22
|
+
| File | Size | Use |
|
|
23
|
+
| ------------------------------ | ---------- | --------------------------------------------------------- |
|
|
24
|
+
| `favicon/favicon.ico` | 16, 32, 48 | The fallback every browser understands |
|
|
25
|
+
| `favicon/favicon.svg` | any | Preferred by current browsers; stays sharp on any display |
|
|
26
|
+
| `favicon/favicon-16.png` | 16 | Explicit small PNG |
|
|
27
|
+
| `favicon/favicon-32.png` | 32 | Explicit standard PNG |
|
|
28
|
+
| `favicon/apple-touch-icon.png` | 180 | iOS home screen |
|
|
29
|
+
| `favicon/icon-192.png` | 192 | Web app manifest |
|
|
30
|
+
| `favicon/icon-512.png` | 512 | Web app manifest, splash screens |
|
|
31
|
+
| `favicon/og-image.png` | 1200×630 | Link previews in chat and social apps |
|
|
32
|
+
|
|
33
|
+
The mark sits on a square brand-blue tile. The white ImFusion glyph is scaled proportionally within it with a small inset at
|
|
34
|
+
the sides.
|
|
35
|
+
|
|
36
|
+
<div style={{ display: "flex", gap: "1.5rem", alignItems: "flex-end", margin: "1.5rem 0" }}>
|
|
37
|
+
{[16, 32, 64, 128].map(size => (
|
|
38
|
+
<div key={size} style={{ textAlign: "center" }}>
|
|
39
|
+
<img src={faviconUrl} width={size} height={size} alt="" style={{ display: "block", marginBottom: "0.5rem" }} />
|
|
40
|
+
<code style={{ fontSize: "0.75rem" }}>{size}</code>
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<img src={ogUrl} alt="" width="480" style={{ display: "block", borderRadius: "0.5rem", margin: "1.5rem 0" }} />
|
|
46
|
+
|
|
47
|
+
## Use the files
|
|
48
|
+
|
|
49
|
+
Pick the method that matches how the application builds:
|
|
50
|
+
|
|
51
|
+
| Method | Use it when | Public URLs |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| [Vite plugin](#vite-plugin) | The application builds with Vite | Stable root paths such as `/favicon.ico` |
|
|
54
|
+
| [Copy step](#copy-step) | Another build system, or a static directory served as is | Whatever the copy target gives them |
|
|
55
|
+
| [Direct import](#direct-import) | One file used from application code, such as a manifest or an `<img>` | Hashed by the bundler |
|
|
56
|
+
|
|
57
|
+
### Vite plugin
|
|
58
|
+
|
|
59
|
+
Register the plugin, then reference the stable root URLs from the document head:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { defineConfig } from "vite";
|
|
63
|
+
import { imfusionBrandAssets } from "@imfusion/web-ui/vite";
|
|
64
|
+
|
|
65
|
+
export default defineConfig({
|
|
66
|
+
plugins: [imfusionBrandAssets()]
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<link rel="icon" href="/favicon.ico" sizes="48x48" />
|
|
72
|
+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
73
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
74
|
+
<meta property="og:image" content="https://example.com/og-image.png" />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The plugin serves the complete favicon set during development and emits it at the root of the build output. Give `og:image`
|
|
78
|
+
an absolute URL. Chat and social applications fetch it from their own servers, so a relative path does not resolve.
|
|
79
|
+
|
|
80
|
+
### Copy step
|
|
81
|
+
|
|
82
|
+
Copy the files into the application's static-files directory during its build, then reference them from its HTML the same way
|
|
83
|
+
as above. Resolve the package path rather than hard-coding a path into `node_modules`:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { dirname } from "node:path";
|
|
87
|
+
import { fileURLToPath } from "node:url";
|
|
88
|
+
|
|
89
|
+
const faviconDir = dirname(fileURLToPath(import.meta.resolve("@imfusion/web-ui/assets/favicon/favicon.svg")));
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Direct import
|
|
93
|
+
|
|
94
|
+
A bundler can also take one file directly:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import iconUrl from "@imfusion/web-ui/assets/favicon/icon-512.png";
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The bundler returns the asset's URL for use in application code. Vite also processes `index.html` and rewrites supported
|
|
101
|
+
asset references there. Use the plugin or the copy step when the files need stable public filenames.
|
|
102
|
+
|
|
103
|
+
<PageNav />
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
|
|
3
|
+
import { MarkdownDoc } from "#/storybook/docs/markdown-doc";
|
|
4
|
+
import { DocParagraph } from "#/storybook/docs/screenshot-carousel/doc-paragraph";
|
|
5
|
+
import { Separator } from "#/components/separator";
|
|
6
|
+
import { Typo } from "#/components/typo";
|
|
7
|
+
|
|
8
|
+
import doc from "../../CHANGELOG.md?raw";
|
|
9
|
+
|
|
10
|
+
<Meta title="User Guide/Changelog" />
|
|
11
|
+
|
|
12
|
+
<MarkdownDoc sourcePath="CHANGELOG.md"
|
|
13
|
+
components={{
|
|
14
|
+
h1: <Typo.Hero />,
|
|
15
|
+
h2: <Typo.H1 />,
|
|
16
|
+
h3: <Typo.H2 />,
|
|
17
|
+
h4: <Typo.H3 variant="primary" />,
|
|
18
|
+
p: <DocParagraph variant="primary" />,
|
|
19
|
+
ul: <Typo.UnorderedList variant="primary" />,
|
|
20
|
+
hr: <Separator spacing="spacious" variant="dashed" />
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
{doc}
|
|
24
|
+
</MarkdownDoc>
|
|
25
|
+
|
|
26
|
+
<PageNav />
|