@imfusion/web-ui 0.6.4-dev.8.g41d03f2f → 0.6.4-dev.84.gbfc0d437
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 +19 -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-DIe1Utiy.js → tabs-By5ir2pt.js} +162 -159
- 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 +127 -0
- package/docs/user-guide/BrandAssets.mdx +102 -0
- package/docs/user-guide/Changelog.mdx +23 -0
- package/docs/user-guide/GettingStarted.mdx +70 -0
- package/docs/user-guide/HowItsBuilt.mdx +113 -0
- package/docs/user-guide/Tokens.mdx +87 -0
- package/docs/user-guide/UsagePatterns.mdx +122 -0
- package/package.json +27 -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 +4 -1
- package/src/llms/skills/imf-web-ui-conventions/SKILL.md +2 -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 +36 -14
- package/src/llms/skills/imf-web-ui-ux/SKILL.md +5 -3
- package/src/llms/tokens.gen.json +71 -7
- 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/{meta-CySnRuVp.js → meta-j-7HGrWv.js} +0 -0
package/bin/install.js
DELETED
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// Installs this package's agent tooling into the consumer project: the
|
|
4
|
-
// skill family (../src/llms/skills/imf-web-ui*/) and the agent lifecycle
|
|
5
|
-
// hooks in the installer-owned template home. Runs in the
|
|
6
|
-
// CONSUMER's environment, so it may only use this package's real runtime
|
|
7
|
-
// dependencies (@clack/prompts). Never wire this into a postinstall hook:
|
|
8
|
-
// ambient script execution on `npm install` is a live supply-chain attack
|
|
9
|
-
// vector — install stays an explicit, user-run command.
|
|
10
|
-
//
|
|
11
|
-
// The skills cross-reference each other, so they install as one bundle.
|
|
12
|
-
// When both targets are selected, .agents/skills/ holds the real copy and
|
|
13
|
-
// .claude/skills/ symlinks it (this repo's own convention) so the two
|
|
14
|
-
// can't drift apart.
|
|
15
|
-
//
|
|
16
|
-
// Flags: bare = help; --hooks adds the agent lifecycle hooks after the consumer
|
|
17
|
-
// checks the repo's existing hooks and still installs the skills.
|
|
18
|
-
// --target claude|agents|all (repeatable) selects scripted install targets,
|
|
19
|
-
// --reconfigure to re-open the target prompt on an existing install.
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
chmodSync,
|
|
23
|
-
cpSync,
|
|
24
|
-
existsSync,
|
|
25
|
-
lstatSync,
|
|
26
|
-
mkdirSync,
|
|
27
|
-
readdirSync,
|
|
28
|
-
readFileSync,
|
|
29
|
-
rmSync,
|
|
30
|
-
symlinkSync,
|
|
31
|
-
writeFileSync
|
|
32
|
-
} from "node:fs";
|
|
33
|
-
import { spawnSync } from "node:child_process";
|
|
34
|
-
import { dirname, relative, resolve } from "node:path";
|
|
35
|
-
import { fileURLToPath } from "node:url";
|
|
36
|
-
import * as p from "@clack/prompts";
|
|
37
|
-
|
|
38
|
-
const SKILL_PREFIX = "imf-web-ui";
|
|
39
|
-
const VERSION_MARKER = ".imf-web-ui-skill-version.json";
|
|
40
|
-
|
|
41
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
42
|
-
// Source is relative to THIS SCRIPT's location (inside node_modules), not
|
|
43
|
-
// the consumer's cwd — the script is invoked from the consumer's project
|
|
44
|
-
// root, but the skills it copies ship alongside this file in the package.
|
|
45
|
-
const skillsRoot = resolve(here, "..", "src", "llms", "skills");
|
|
46
|
-
const packageJson = JSON.parse(readFileSync(resolve(here, "..", "package.json"), "utf-8"));
|
|
47
|
-
const currentVersion = packageJson.version;
|
|
48
|
-
|
|
49
|
-
// Destination is relative to the consumer's project root (cwd), since
|
|
50
|
-
// that's where their `.claude/` or `.agents/` directory lives.
|
|
51
|
-
const projectRoot = process.cwd();
|
|
52
|
-
|
|
53
|
-
const TARGETS = {
|
|
54
|
-
claude: { label: "Claude Code", root: resolve(projectRoot, ".claude", "skills") },
|
|
55
|
-
agents: { label: "Vendor-neutral (.agents/)", root: resolve(projectRoot, ".agents", "skills") }
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
function discoverSkills() {
|
|
59
|
-
if (!existsSync(skillsRoot)) return [];
|
|
60
|
-
return readdirSync(skillsRoot, { withFileTypes: true })
|
|
61
|
-
.filter(entry => entry.isDirectory() && entry.name.startsWith(SKILL_PREFIX))
|
|
62
|
-
.map(entry => entry.name)
|
|
63
|
-
.sort();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function displayPath(absPath) {
|
|
67
|
-
return `./${relative(projectRoot, absPath)}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function readInstalledVersion(dir) {
|
|
71
|
-
const markerPath = resolve(dir, VERSION_MARKER);
|
|
72
|
-
if (!existsSync(markerPath)) return null;
|
|
73
|
-
try {
|
|
74
|
-
return JSON.parse(readFileSync(markerPath, "utf-8")).version ?? null;
|
|
75
|
-
} catch {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function writeRealCopy(sourceDir, destDir) {
|
|
81
|
-
mkdirSync(dirname(destDir), { recursive: true });
|
|
82
|
-
// force: true makes re-running after a version bump overwrite cleanly.
|
|
83
|
-
cpSync(sourceDir, destDir, { recursive: true, force: true });
|
|
84
|
-
writeFileSync(resolve(destDir, VERSION_MARKER), JSON.stringify({ version: currentVersion }, null, 2) + "\n");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function writeSymlink(linkPath, targetPath) {
|
|
88
|
-
mkdirSync(dirname(linkPath), { recursive: true });
|
|
89
|
-
// lstat (not existsSync, which follows symlinks) catches a broken/stale
|
|
90
|
-
// symlink left over from a prior run, not just a real file or directory.
|
|
91
|
-
if (lstatSync(linkPath, { throwIfNoEntry: false })) {
|
|
92
|
-
rmSync(linkPath, { recursive: true, force: true });
|
|
93
|
-
}
|
|
94
|
-
symlinkSync(relative(dirname(linkPath), targetPath), linkPath);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function removeOrphanedSkills(selected, skills, both) {
|
|
98
|
-
const bundledSkills = new Set(skills);
|
|
99
|
-
const selectedTargets = both ? ["agents", "claude"] : selected;
|
|
100
|
-
|
|
101
|
-
for (const key of selectedTargets) {
|
|
102
|
-
const { root } = TARGETS[key];
|
|
103
|
-
if (!existsSync(root)) continue;
|
|
104
|
-
|
|
105
|
-
const orphanedSkills = readdirSync(root).filter(name => name.startsWith(`${SKILL_PREFIX}-`) && !bundledSkills.has(name));
|
|
106
|
-
for (const name of orphanedSkills) {
|
|
107
|
-
rmSync(resolve(root, name), { recursive: true, force: true });
|
|
108
|
-
// In the two-target layout, .claude/ is a symlinked view of the
|
|
109
|
-
// canonical .agents/ copy. Remove its matching link along with the
|
|
110
|
-
// orphaned canonical skill.
|
|
111
|
-
if (both && key === "agents") {
|
|
112
|
-
rmSync(resolve(TARGETS.claude.root, name), { recursive: true, force: true });
|
|
113
|
-
}
|
|
114
|
-
p.log.info(`Removed orphaned skill: ${name} (no longer in this bundle)`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// A prior install is recoverable from the filesystem, so a re-run doesn't
|
|
120
|
-
// re-ask: a target counts as chosen when any bundled skill is present under
|
|
121
|
-
// it. Symlinks count — they're how the both-targets layout represents
|
|
122
|
-
// .claude/, and lstat avoids following them into the real copy.
|
|
123
|
-
function detectInstalledTargets(skills) {
|
|
124
|
-
return Object.keys(TARGETS).filter(key =>
|
|
125
|
-
skills.some(name => lstatSync(resolve(TARGETS[key].root, name), { throwIfNoEntry: false }))
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Hook templates are installer-owned; the installer copies the scripts into the
|
|
130
|
-
// consumer's .agents/hooks/ and merges the registrations into the host
|
|
131
|
-
// registration files: .claude/settings.json from settings.json, .codex/hooks.json
|
|
132
|
-
// from codex-hooks.json. One pristine template per host, merged verbatim.
|
|
133
|
-
const installTemplatesRoot = resolve(here, "..", "src", "llms", "install-templates");
|
|
134
|
-
const hooksSourceDir = resolve(installTemplatesRoot, "hooks");
|
|
135
|
-
const hooksSettingsTemplate = resolve(installTemplatesRoot, "settings.json");
|
|
136
|
-
const codexHooksTemplate = resolve(installTemplatesRoot, "codex-hooks.json");
|
|
137
|
-
// Installer-owned subdirectory: refreshed wholesale on every run, so a repo's
|
|
138
|
-
// own hooks in .agents/hooks/ are never touched.
|
|
139
|
-
const hooksDestDir = resolve(projectRoot, ".agents", "hooks", "imf-web-ui");
|
|
140
|
-
const settingsPath = resolve(projectRoot, ".claude", "settings.json");
|
|
141
|
-
const codexHooksPath = resolve(projectRoot, ".codex", "hooks.json");
|
|
142
|
-
|
|
143
|
-
// Serializing the file reflows entries this installer doesn't own; the
|
|
144
|
-
// consumer's own Prettier puts them back. Best-effort: absent, or the path is
|
|
145
|
-
// .prettierignore'd, nothing happens.
|
|
146
|
-
function formatSettings(path) {
|
|
147
|
-
let toolRoot = projectRoot;
|
|
148
|
-
while (toolRoot !== dirname(toolRoot) && !existsSync(resolve(toolRoot, "node_modules", ".bin"))) {
|
|
149
|
-
toolRoot = dirname(toolRoot);
|
|
150
|
-
}
|
|
151
|
-
const prettier = resolve(toolRoot, "node_modules", ".bin", "prettier");
|
|
152
|
-
if (!existsSync(prettier)) return;
|
|
153
|
-
spawnSync(prettier, ["--write", path], { cwd: projectRoot, stdio: "ignore" });
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function installHookScripts() {
|
|
157
|
-
const scripts = readdirSync(hooksSourceDir).filter(name => name.endsWith(".sh"));
|
|
158
|
-
mkdirSync(hooksDestDir, { recursive: true });
|
|
159
|
-
for (const name of scripts) {
|
|
160
|
-
const dest = resolve(hooksDestDir, name);
|
|
161
|
-
cpSync(resolve(hooksSourceDir, name), dest, { force: true });
|
|
162
|
-
chmodSync(dest, 0o755);
|
|
163
|
-
}
|
|
164
|
-
// The directory is installer-owned: a script dropped from the template is
|
|
165
|
-
// removed on refresh, so a stale hook can't keep running after an upgrade.
|
|
166
|
-
const removed = readdirSync(hooksDestDir).filter(name => name.endsWith(".sh") && !scripts.includes(name));
|
|
167
|
-
for (const name of removed) {
|
|
168
|
-
rmSync(resolve(hooksDestDir, name), { force: true });
|
|
169
|
-
}
|
|
170
|
-
return { installed: scripts, removed };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// A registration is identified by the script it runs, not the command string:
|
|
174
|
-
// "$VAR/..." and "${VAR}/..." are the same hook.
|
|
175
|
-
const HOOKS_DIR_SEGMENT = ".agents/hooks/imf-web-ui/";
|
|
176
|
-
|
|
177
|
-
function hookScriptId(command) {
|
|
178
|
-
if (typeof command !== "string") return null;
|
|
179
|
-
const at = command.lastIndexOf(HOOKS_DIR_SEGMENT);
|
|
180
|
-
if (at === -1) return null;
|
|
181
|
-
// Only a bare script name (plus an optional closing quote) identifies a
|
|
182
|
-
// registration as installer-owned. Trailing arguments or redirection mean
|
|
183
|
-
// the consumer customized the command; that counts as foreign, so the
|
|
184
|
-
// stale-prune below can never delete it.
|
|
185
|
-
const match = command.slice(at + HOOKS_DIR_SEGMENT.length).match(/^([\w.-]+)["']?$/);
|
|
186
|
-
return match ? match[1] : null;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function entryScriptIds(entry) {
|
|
190
|
-
return (entry.hooks ?? []).map(hook => hookScriptId(hook.command)).filter(Boolean);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Merge, never clobber: a registration is added only when no existing entry
|
|
194
|
-
// for that event already runs the same script, so re-runs are idempotent and
|
|
195
|
-
// hand-written settings survive. The one exception is entries that only run
|
|
196
|
-
// scripts from the installer-owned directory that are no longer shipped —
|
|
197
|
-
// those are pruned, or they would point at a script the refresh just removed.
|
|
198
|
-
// A run that changes nothing writes nothing.
|
|
199
|
-
function mergeHookRegistrations(targetPath, templateHooks, shippedScripts) {
|
|
200
|
-
let settings = {};
|
|
201
|
-
if (existsSync(targetPath)) {
|
|
202
|
-
try {
|
|
203
|
-
settings = JSON.parse(readFileSync(targetPath, "utf-8"));
|
|
204
|
-
} catch {
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
settings.hooks ??= {};
|
|
209
|
-
let added = 0;
|
|
210
|
-
let removed = 0;
|
|
211
|
-
// Pruning is per hook, not per entry: only an individual hook that
|
|
212
|
-
// unambiguously runs a no-longer-shipped installer-owned script goes;
|
|
213
|
-
// consumer hooks sharing the same entry stay.
|
|
214
|
-
const emptiedEvents = [];
|
|
215
|
-
for (const [event, entries] of Object.entries(settings.hooks)) {
|
|
216
|
-
if (!Array.isArray(entries)) continue;
|
|
217
|
-
let pruned = 0;
|
|
218
|
-
const kept = entries
|
|
219
|
-
.map(entry => {
|
|
220
|
-
if (!Array.isArray(entry.hooks)) return entry;
|
|
221
|
-
const hooks = entry.hooks.filter(hook => {
|
|
222
|
-
const script = hookScriptId(hook.command);
|
|
223
|
-
return script === null || shippedScripts.includes(script);
|
|
224
|
-
});
|
|
225
|
-
pruned += entry.hooks.length - hooks.length;
|
|
226
|
-
return hooks.length === entry.hooks.length ? entry : { ...entry, hooks };
|
|
227
|
-
})
|
|
228
|
-
.filter(entry => !Array.isArray(entry.hooks) || entry.hooks.length > 0);
|
|
229
|
-
if (pruned === 0) continue;
|
|
230
|
-
removed += pruned;
|
|
231
|
-
if (kept.length === 0 && !(event in templateHooks)) emptiedEvents.push(event);
|
|
232
|
-
else settings.hooks[event] = kept;
|
|
233
|
-
}
|
|
234
|
-
// An event whose entries were all pruned disappears instead of lingering as [].
|
|
235
|
-
if (emptiedEvents.length > 0) {
|
|
236
|
-
settings.hooks = Object.fromEntries(Object.entries(settings.hooks).filter(([event]) => !emptiedEvents.includes(event)));
|
|
237
|
-
}
|
|
238
|
-
for (const [event, entries] of Object.entries(templateHooks)) {
|
|
239
|
-
settings.hooks[event] ??= [];
|
|
240
|
-
for (const entry of entries) {
|
|
241
|
-
const scripts = entryScriptIds(entry);
|
|
242
|
-
const present = settings.hooks[event].some(existing =>
|
|
243
|
-
entryScriptIds(existing).some(script => scripts.includes(script))
|
|
244
|
-
);
|
|
245
|
-
if (!present) {
|
|
246
|
-
settings.hooks[event].push(entry);
|
|
247
|
-
added++;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (added === 0 && removed === 0) return { added, removed };
|
|
252
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
253
|
-
writeFileSync(targetPath, JSON.stringify(settings, null, 2) + "\n");
|
|
254
|
-
formatSettings(targetPath);
|
|
255
|
-
return { added, removed };
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// The AGENTS.md baseline note lives inside a fenced block owned by this
|
|
259
|
-
// installer. The template between the markers is the source of truth; an
|
|
260
|
-
// existing AGENTS.md gets the block replaced in place (or appended when
|
|
261
|
-
// absent), everything outside the fence is untouched. No AGENTS.md at all
|
|
262
|
-
// is left alone — scaffolding one is the setup skill's job.
|
|
263
|
-
const AGENTS_BLOCK_BEGIN = "<!-- imf-web-ui:begin";
|
|
264
|
-
const AGENTS_BLOCK_END = "<!-- imf-web-ui:end -->";
|
|
265
|
-
const agentsTemplatePath = resolve(installTemplatesRoot, "AGENTS.md");
|
|
266
|
-
const agentsPath = resolve(projectRoot, "AGENTS.md");
|
|
267
|
-
|
|
268
|
-
function extractAgentsBlock(content) {
|
|
269
|
-
const begin = content.indexOf(AGENTS_BLOCK_BEGIN);
|
|
270
|
-
const end = content.indexOf(AGENTS_BLOCK_END);
|
|
271
|
-
if (begin === -1 || end === -1) return null;
|
|
272
|
-
return content.slice(begin, end + AGENTS_BLOCK_END.length);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function upsertAgentsBlock() {
|
|
276
|
-
if (!existsSync(agentsPath)) return "absent";
|
|
277
|
-
const block = extractAgentsBlock(readFileSync(agentsTemplatePath, "utf-8"));
|
|
278
|
-
if (!block) return "absent";
|
|
279
|
-
const current = readFileSync(agentsPath, "utf-8");
|
|
280
|
-
const existing = extractAgentsBlock(current);
|
|
281
|
-
const next = existing ? current.replace(existing, block) : `${current.trimEnd()}\n\n${block}\n`;
|
|
282
|
-
if (next === current) return "unchanged";
|
|
283
|
-
writeFileSync(agentsPath, next);
|
|
284
|
-
return existing ? "updated" : "added";
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// --target claude|agents|all (repeatable) selects targets without the
|
|
288
|
-
// interactive prompt — for CI and scripted installs.
|
|
289
|
-
function parseTargetFlags(argv) {
|
|
290
|
-
const targets = [];
|
|
291
|
-
for (let i = 0; i < argv.length; i++) {
|
|
292
|
-
if (argv[i] !== "--target") continue;
|
|
293
|
-
const value = argv[i + 1];
|
|
294
|
-
if (!value || (value !== "all" && !(value in TARGETS))) {
|
|
295
|
-
console.error(`--target expects one of: ${Object.keys(TARGETS).join(", ")}, all`);
|
|
296
|
-
process.exit(1);
|
|
297
|
-
}
|
|
298
|
-
targets.push(...(value === "all" ? ["agents", "claude"] : [value]));
|
|
299
|
-
i++;
|
|
300
|
-
}
|
|
301
|
-
return [...new Set(targets)];
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function printHelp() {
|
|
305
|
-
console.log(`Usage: web-ui-install [options]
|
|
306
|
-
|
|
307
|
-
Install @imfusion/web-ui agent skills into a consumer project.
|
|
308
|
-
|
|
309
|
-
Options:
|
|
310
|
-
--target agents|claude|all Install into selected skill target(s).
|
|
311
|
-
Repeat --target to select multiple targets.
|
|
312
|
-
--hooks Install agent lifecycle hooks.
|
|
313
|
-
--reconfigure Re-open target selection prompt.
|
|
314
|
-
-h, --help Show this help.
|
|
315
|
-
`);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async function main() {
|
|
319
|
-
const argv = process.argv.slice(2);
|
|
320
|
-
if (argv.length === 0 || argv.includes("-h") || argv.includes("--help")) {
|
|
321
|
-
printHelp();
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const skills = discoverSkills();
|
|
326
|
-
const flagTargets = parseTargetFlags(argv);
|
|
327
|
-
const reconfigure = argv.includes("--reconfigure");
|
|
328
|
-
// Every install includes the skill bundle. Hooks are opt-in via --hooks —
|
|
329
|
-
// the consumer's setup workflow drives that after checking what the repo
|
|
330
|
-
// already registers, but hooks without their companion skills are invalid.
|
|
331
|
-
const wantHooks = argv.includes("--hooks");
|
|
332
|
-
const wantSkills = true;
|
|
333
|
-
|
|
334
|
-
const components = [wantSkills && `${skills.length} skills`, wantHooks && "agent hooks"].filter(Boolean);
|
|
335
|
-
p.intro(`@imfusion/web-ui install — ${components.join(" + ")}`);
|
|
336
|
-
|
|
337
|
-
if (wantSkills && skills.length === 0) {
|
|
338
|
-
p.log.error(`No skills found at ${skillsRoot}. Reinstall @imfusion/web-ui and try again.`);
|
|
339
|
-
p.outro("Nothing installed.");
|
|
340
|
-
process.exitCode = 1;
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (wantHooks) {
|
|
345
|
-
const { installed, removed } = installHookScripts();
|
|
346
|
-
p.log.success(`Agent hooks -> ${displayPath(hooksDestDir)} (${installed.length} scripts)`);
|
|
347
|
-
if (removed.length > 0) {
|
|
348
|
-
p.log.info(`Removed stale hook script(s): ${removed.join(", ")} (no longer shipped)`);
|
|
349
|
-
}
|
|
350
|
-
const registrations = [
|
|
351
|
-
[settingsPath, JSON.parse(readFileSync(hooksSettingsTemplate, "utf-8")).hooks],
|
|
352
|
-
[codexHooksPath, JSON.parse(readFileSync(codexHooksTemplate, "utf-8")).hooks]
|
|
353
|
-
];
|
|
354
|
-
for (const [path, hooks] of registrations) {
|
|
355
|
-
const result = mergeHookRegistrations(path, hooks, installed);
|
|
356
|
-
if (result === null) {
|
|
357
|
-
p.log.warn(`${displayPath(path)} is not valid JSON — registrations not merged, fix it and re-run.`);
|
|
358
|
-
} else if (result.added > 0 || result.removed > 0) {
|
|
359
|
-
const changes = [
|
|
360
|
-
result.added > 0 && `registered ${result.added}`,
|
|
361
|
-
result.removed > 0 && `removed ${result.removed} stale`
|
|
362
|
-
].filter(Boolean);
|
|
363
|
-
p.log.success(`${changes.join(", ")} hook registration(s) in ${displayPath(path)}`);
|
|
364
|
-
} else {
|
|
365
|
-
p.log.info(`Hook registrations already present in ${displayPath(path)}`);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const installedTargets = detectInstalledTargets(skills);
|
|
371
|
-
|
|
372
|
-
const existingVersions = Object.values(TARGETS)
|
|
373
|
-
.flatMap(({ root }) => skills.map(name => readInstalledVersion(resolve(root, name))))
|
|
374
|
-
.filter(Boolean);
|
|
375
|
-
if (existingVersions.length > 0 && existingVersions.every(v => v === currentVersion)) {
|
|
376
|
-
p.log.info(`Already up to date (v${currentVersion}). Re-running will overwrite with the same content.`);
|
|
377
|
-
} else if (existingVersions.some(v => v !== currentVersion)) {
|
|
378
|
-
const from = existingVersions.find(v => v !== currentVersion);
|
|
379
|
-
p.log.info(`Updating installed skills from v${from} to v${currentVersion}.`);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
p.log.message(`Skills in this bundle:\n${skills.map(name => ` - ${name}`).join("\n")}`);
|
|
383
|
-
|
|
384
|
-
let selected;
|
|
385
|
-
if (flagTargets.length > 0) {
|
|
386
|
-
selected = flagTargets;
|
|
387
|
-
p.log.info(`Targets from --target flags: ${selected.join(", ")}`);
|
|
388
|
-
} else if (installedTargets.length > 0 && !reconfigure) {
|
|
389
|
-
selected = installedTargets;
|
|
390
|
-
p.log.info(
|
|
391
|
-
`Refreshing the existing install: ${selected.map(key => displayPath(TARGETS[key].root)).join(", ")}` +
|
|
392
|
-
` — pass --reconfigure to choose different targets.`
|
|
393
|
-
);
|
|
394
|
-
} else {
|
|
395
|
-
selected = await p.multiselect({
|
|
396
|
-
message: "Install into which skill directory (or directories)?",
|
|
397
|
-
options: Object.entries(TARGETS).map(([key, { label, root }]) => ({
|
|
398
|
-
value: key,
|
|
399
|
-
label,
|
|
400
|
-
hint: displayPath(root)
|
|
401
|
-
})),
|
|
402
|
-
required: true
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
if (p.isCancel(selected)) {
|
|
406
|
-
p.cancel("Cancelled — nothing installed.");
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const both = selected.includes("claude") && selected.includes("agents");
|
|
412
|
-
|
|
413
|
-
for (const name of skills) {
|
|
414
|
-
const sourceDir = resolve(skillsRoot, name);
|
|
415
|
-
if (both) {
|
|
416
|
-
// .agents/ is the canonical real copy; .claude/ aliases it via symlink.
|
|
417
|
-
const realDir = resolve(TARGETS.agents.root, name);
|
|
418
|
-
writeRealCopy(sourceDir, realDir);
|
|
419
|
-
writeSymlink(resolve(TARGETS.claude.root, name), realDir);
|
|
420
|
-
} else {
|
|
421
|
-
for (const key of selected) {
|
|
422
|
-
writeRealCopy(sourceDir, resolve(TARGETS[key].root, name));
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
removeOrphanedSkills(selected, skills, both);
|
|
428
|
-
|
|
429
|
-
if (both) {
|
|
430
|
-
p.log.success(`Vendor-neutral (.agents/) -> ${displayPath(TARGETS.agents.root)}/${SKILL_PREFIX}*`);
|
|
431
|
-
p.log.success(`Claude Code -> ${displayPath(TARGETS.claude.root)}/${SKILL_PREFIX}* (symlinks -> .agents/)`);
|
|
432
|
-
} else {
|
|
433
|
-
for (const key of selected) {
|
|
434
|
-
p.log.success(`${TARGETS[key].label} -> ${displayPath(TARGETS[key].root)}/${SKILL_PREFIX}*`);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
const agentsResult = upsertAgentsBlock();
|
|
439
|
-
if (agentsResult === "updated" || agentsResult === "added") {
|
|
440
|
-
p.log.success(`Refreshed the imf-web-ui block in ${displayPath(agentsPath)}`);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
p.outro(`Done — ${skills.length} skills installed${wantHooks ? " + agent hooks" : ""}.`);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
await main();
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Plugin } from 'vite';
|
|
2
|
-
export interface ReadableCssModuleNamesOptions {
|
|
3
|
-
/** Namespace for every generated class name. Short, stable, app-scoped (`imf-ui`, `acme`). */
|
|
4
|
-
prefix: string;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Generates readable CSS Module class names instead of the default hash, so the DOM
|
|
8
|
-
* stays legible in devtools and browser automation. `button.module.css` yields
|
|
9
|
-
* `{prefix}-button-module-root` — `[name]` is the filename, which keeps its `.module`
|
|
10
|
-
* suffix.
|
|
11
|
-
*
|
|
12
|
-
* Register it in every tool that compiles the CSS (the app build AND Storybook). A
|
|
13
|
-
* compiler left out generates different class names for the same source file and its
|
|
14
|
-
* styles silently don't apply.
|
|
15
|
-
*
|
|
16
|
-
* Defaults to opting into Lightning CSS, which drops `css.modules` options it has no
|
|
17
|
-
* equivalent for (`localsConvention`). Set `css.transformer: "postcss"` to stay on
|
|
18
|
-
* the PostCSS pipeline.
|
|
19
|
-
*/
|
|
20
|
-
export declare function readableCssModuleNames({ prefix }: ReadableCssModuleNamesOptions): Plugin;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
//#region src/build/vite-css-module-names/index.ts
|
|
2
|
-
function e(e, t) {
|
|
3
|
-
let { lightningcss: n, modules: r, transformer: i } = e.css ?? {}, a = n?.cssModules;
|
|
4
|
-
if (!(typeof a == "boolean" || r === !1) && !a?.pattern && !(typeof r == "object" && r.generateScopedName)) return i === "postcss" ? { css: { modules: { generateScopedName: t } } } : { css: {
|
|
5
|
-
transformer: "lightningcss",
|
|
6
|
-
lightningcss: { cssModules: { pattern: t } }
|
|
7
|
-
} };
|
|
8
|
-
}
|
|
9
|
-
function t({ prefix: t }) {
|
|
10
|
-
let n = `${t}-[name]-[local]`;
|
|
11
|
-
return {
|
|
12
|
-
name: "imf-ui:readable-css-module-names",
|
|
13
|
-
config: (t) => e(t, n)
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
|
-
export { t as readableCssModuleNames };
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spinner geometry — the animated ImFusion glyph.
|
|
3
|
-
*
|
|
4
|
-
* The mark is two filled bands separated by an empty diagonal channel (see
|
|
5
|
-
* `logo/imfusion/imfusion.tsx`). This module reproduces that structure as a
|
|
6
|
-
* *morphing* shape: the channel rotates, breathes (its width pulses, never
|
|
7
|
-
* closing), and its interior nodes sway — so the loader reads as the mark
|
|
8
|
-
* alive, not a generic ring.
|
|
9
|
-
*
|
|
10
|
-
* The motion is a live per-frame morph: a single `requestAnimationFrame` loop
|
|
11
|
-
* in the component (spinner.tsx) calls {@link frameGeometry} each frame and
|
|
12
|
-
* writes the two returned path `d` strings straight onto the SVG paths — no
|
|
13
|
-
* React re-render per frame, no SMIL. (SMIL/CSS `d` morphing was tried and
|
|
14
|
-
* dropped: neither is GPU-composited, and CSS `d` isn't supported in Safari.)
|
|
15
|
-
*
|
|
16
|
-
* Everything is tunable from the {@link KNOBS} block. The values there were
|
|
17
|
-
* dialled in against rendered evidence at 16/20/24/96px; the shape is a
|
|
18
|
-
* balance — extreme values pinch the channel shut or spike the bands, so nudge
|
|
19
|
-
* rather than leap. `frameGeometry(t)` is pure and deterministic: feed it a
|
|
20
|
-
* time in ms and it returns the two band path `d`s for that instant.
|
|
21
|
-
*/
|
|
22
|
-
/** Design-space edge length; the SVG viewBox is `0 0 D D`. */
|
|
23
|
-
export declare const D = 100;
|
|
24
|
-
/**
|
|
25
|
-
* The two band path `d` strings at time `tms` (ms) — the shape at any instant
|
|
26
|
-
* of the loop, as `[topBand, bottomBand]`. Pure and deterministic; the rAF loop
|
|
27
|
-
* in the component calls this each frame and writes the results to the paths.
|
|
28
|
-
*/
|
|
29
|
-
export declare function frameGeometry(tms: number): [string, string];
|
|
30
|
-
/**
|
|
31
|
-
* The period over which the whole composition returns to its start pose — the
|
|
32
|
-
* least-common-multiple-ish window of rotation, breathe, and sway. Rotation is
|
|
33
|
-
* `ROT_S`; breathe/sway are shorter and phase-relative. We loop over the
|
|
34
|
-
* rotation period and let breathe/sway ride within it; `SWAY_RATIO` is chosen
|
|
35
|
-
* so the combined motion reads continuous rather than obviously repeating.
|
|
36
|
-
*/
|
|
37
|
-
export declare const LOOP_MS: number;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const cmake: import('@tanstack/highlight/core').LanguageDefinition<"cmake">;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const cpp: import('@tanstack/highlight/core').LanguageDefinition<"cpp">;
|
|
File without changes
|