@imfusion/web-ui 0.6.1-dev.6.g4adc5c3e → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +30 -0
- package/README.md +104 -171
- package/THIRD_PARTY_NOTICES.md +34 -0
- package/bin/install.js +28 -10
- package/dist/code-BFMQnmu9.js +147 -0
- package/dist/codegen/gen-code-highlight-theme.d.ts +1 -0
- package/dist/components/code/code.d.ts +5 -4
- package/dist/components/stack/stack.d.ts +1 -1
- package/dist/components/toast/index.d.ts +2 -0
- package/dist/components/toast/toast.d.ts +200 -0
- package/dist/components/toast/toast.meta.d.ts +2 -0
- package/dist/components/typo/typo.d.ts +23 -22
- package/dist/icons/icon-config.d.ts +12 -0
- package/dist/{icons-wBmF0U2x.js → icons-Cy1HAosO.js} +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1278 -1069
- package/dist/integrations/code-highlight/highlighter.d.ts +24 -0
- package/dist/integrations/code-highlight.js +80 -47
- package/dist/integrations/image-display-options.js +2 -2
- package/dist/provider/web-ui-provider.d.ts +3 -3
- package/dist/style.css +1 -1
- package/dist/{tabs-CMKvMF4E.js → tabs-DIe1Utiy.js} +2 -0
- package/docs/assets/imfusion-banner.svg +16 -0
- package/package.json +15 -8
- package/src/docgen/doc.gen.json +515 -1
- package/src/llms/install-templates/AGENTS.md +15 -18
- package/src/llms/llms.gen.txt +39 -33
- package/src/llms/skills/imf-web-ui/SKILL.md +30 -39
- package/src/llms/skills/imf-web-ui-audit/SKILL.md +50 -102
- package/src/llms/skills/imf-web-ui-components/SKILL.md +47 -104
- package/src/llms/skills/imf-web-ui-conventions/SKILL.md +44 -52
- package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +1 -0
- package/src/llms/skills/imf-web-ui-conventions/topics/agent-tooling.md +40 -62
- package/src/llms/skills/imf-web-ui-conventions/topics/assets.md +11 -12
- package/src/llms/skills/imf-web-ui-conventions/topics/authentication.md +31 -46
- package/src/llms/skills/imf-web-ui-conventions/topics/class-names.md +18 -23
- package/src/llms/skills/imf-web-ui-conventions/topics/components.md +20 -69
- package/src/llms/skills/imf-web-ui-conventions/topics/data.md +50 -146
- package/src/llms/skills/imf-web-ui-conventions/topics/docs-structure.md +17 -23
- package/src/llms/skills/imf-web-ui-conventions/topics/git.md +15 -20
- package/src/llms/skills/imf-web-ui-conventions/topics/library-boundary.md +28 -19
- package/src/llms/skills/imf-web-ui-conventions/topics/library-setup.md +20 -16
- package/src/llms/skills/imf-web-ui-conventions/topics/npm-project.md +28 -42
- package/src/llms/skills/imf-web-ui-conventions/topics/project-structure.md +28 -30
- package/src/llms/skills/imf-web-ui-conventions/topics/react.md +28 -74
- package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +65 -62
- package/src/llms/skills/imf-web-ui-conventions/topics/testing.md +12 -14
- package/src/llms/skills/imf-web-ui-conventions/topics/tokens.md +9 -4
- package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +40 -68
- package/src/llms/skills/imf-web-ui-conventions/topics/typescript.md +26 -50
- package/src/llms/skills/imf-web-ui-conventions/topics/validation.md +19 -25
- package/src/llms/skills/imf-web-ui-setup/SKILL.md +45 -64
- package/src/llms/skills/imf-web-ui-update/SKILL.md +48 -114
- package/src/llms/skills/imf-web-ui-ux/SKILL.md +64 -92
- package/src/llms/skills/imf-web-ui-ux/references/forms.md +16 -36
- package/src/llms/skills/imf-web-ui-ux/references/usability-heuristics.md +14 -27
- package/src/llms/skills/imf-web-ui-ux/references/visual-design.md +22 -38
- package/src/llms/tokens.gen.json +5 -5
- package/bin/install.test.ts +0 -329
- package/dist/code-Blo48PGr.js +0 -136
- package/dist/icons/icon-config-provider.d.ts +0 -8
- package/dist/icons/icon-context.d.ts +0 -4
package/bin/install.test.ts
DELETED
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import {
|
|
4
|
-
existsSync,
|
|
5
|
-
lstatSync,
|
|
6
|
-
mkdirSync,
|
|
7
|
-
mkdtempSync,
|
|
8
|
-
readdirSync,
|
|
9
|
-
readFileSync,
|
|
10
|
-
statSync,
|
|
11
|
-
symlinkSync,
|
|
12
|
-
writeFileSync
|
|
13
|
-
} from "node:fs";
|
|
14
|
-
import { tmpdir } from "node:os";
|
|
15
|
-
import { join, resolve } from "node:path";
|
|
16
|
-
import { fileURLToPath } from "node:url";
|
|
17
|
-
|
|
18
|
-
const installer = resolve(fileURLToPath(new URL(".", import.meta.url)), "install.js");
|
|
19
|
-
|
|
20
|
-
function runInstall(cwd: string, ...args: string[]) {
|
|
21
|
-
const result = spawnSync("node", [installer, ...args], { cwd, encoding: "utf-8" });
|
|
22
|
-
expect(result.status).toBe(0);
|
|
23
|
-
return result;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function readSettings(cwd: string) {
|
|
27
|
-
return JSON.parse(readFileSync(join(cwd, ".claude", "settings.json"), "utf-8")) as {
|
|
28
|
-
permissions?: { allow: string[] };
|
|
29
|
-
hooks: Record<string, { hooks: { command?: string }[] }[]>;
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let consumer: string;
|
|
34
|
-
const retiredSkillNames = [
|
|
35
|
-
["agent", "setup"],
|
|
36
|
-
["library", "setup"]
|
|
37
|
-
].map(parts => `imf-web-ui-${parts.join("-")}`);
|
|
38
|
-
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
consumer = mkdtempSync(join(tmpdir(), "web-ui-install-"));
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("web-ui-install", () => {
|
|
44
|
-
it("installs skills into both targets with version markers", () => {
|
|
45
|
-
runInstall(consumer, "--skills", "--target", "agents", "--target", "claude");
|
|
46
|
-
|
|
47
|
-
const installed = readdirSync(join(consumer, ".agents", "skills"));
|
|
48
|
-
expect(installed).toContain("imf-web-ui");
|
|
49
|
-
expect(installed).toContain("imf-web-ui-conventions");
|
|
50
|
-
expect(installed).toContain("imf-web-ui-setup");
|
|
51
|
-
expect(installed).toContain("imf-web-ui-audit");
|
|
52
|
-
expect(installed).toHaveLength(7);
|
|
53
|
-
for (const name of retiredSkillNames) expect(installed).not.toContain(name);
|
|
54
|
-
|
|
55
|
-
for (const name of installed) {
|
|
56
|
-
expect(existsSync(join(consumer, ".agents", "skills", name, ".imf-web-ui-skill-version.json"))).toBe(true);
|
|
57
|
-
expect(lstatSync(join(consumer, ".claude", "skills", name)).isSymbolicLink()).toBe(true);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("removes skills that are no longer included in the bundle", () => {
|
|
62
|
-
const orphan = "imf-web-ui-retired-skill";
|
|
63
|
-
const agentsOrphan = join(consumer, ".agents", "skills", orphan);
|
|
64
|
-
mkdirSync(agentsOrphan, { recursive: true });
|
|
65
|
-
writeFileSync(join(agentsOrphan, "SKILL.md"), "# Retired skill\n");
|
|
66
|
-
const claudeOrphan = join(consumer, ".claude", "skills", orphan);
|
|
67
|
-
mkdirSync(join(consumer, ".claude", "skills"), { recursive: true });
|
|
68
|
-
symlinkSync(agentsOrphan, claudeOrphan);
|
|
69
|
-
expect(lstatSync(claudeOrphan).isSymbolicLink()).toBe(true);
|
|
70
|
-
|
|
71
|
-
const result = runInstall(consumer, "--skills", "--target", "agents", "--target", "claude");
|
|
72
|
-
|
|
73
|
-
expect(existsSync(agentsOrphan)).toBe(false);
|
|
74
|
-
expect(lstatSync(claudeOrphan, { throwIfNoEntry: false })).toBeUndefined();
|
|
75
|
-
expect(result.stdout).toContain(`Removed orphaned skill: ${orphan} (no longer in this bundle)`);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("installs hooks into an installer-owned directory and registers them for both hosts", () => {
|
|
79
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
80
|
-
|
|
81
|
-
const hookDir = join(consumer, ".agents", "hooks", "imf-web-ui");
|
|
82
|
-
const scripts = readdirSync(hookDir);
|
|
83
|
-
expect(scripts).toEqual(expect.arrayContaining(["session-start.sh", "subagent-start.sh", "user-prompt-submit.sh"]));
|
|
84
|
-
for (const script of scripts) {
|
|
85
|
-
expect(statSync(join(hookDir, script)).mode & 0o100).toBeTruthy();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const settings = readSettings(consumer);
|
|
89
|
-
expect(Object.keys(settings.hooks)).toEqual(
|
|
90
|
-
expect.arrayContaining(["SessionStart", "SubagentStart", "UserPromptSubmit"])
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const codexHooks = JSON.parse(readFileSync(join(consumer, ".codex", "hooks.json"), "utf-8")) as {
|
|
94
|
-
hooks: Record<string, { hooks: { command?: string }[] }[]>;
|
|
95
|
-
};
|
|
96
|
-
expect(Object.keys(codexHooks.hooks)).toEqual(Object.keys(settings.hooks));
|
|
97
|
-
const codexCommands = Object.values(codexHooks.hooks).flatMap(entries =>
|
|
98
|
-
entries.flatMap(entry => entry.hooks.map(hook => hook.command))
|
|
99
|
-
);
|
|
100
|
-
for (const command of codexCommands) {
|
|
101
|
-
expect(command).toContain("$(git rev-parse --show-toplevel)/.agents/hooks/imf-web-ui/");
|
|
102
|
-
expect(command).not.toContain("CLAUDE_PROJECT_DIR");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
expect(readdirSync(join(consumer, ".agents", "skills"))).toHaveLength(7);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("prunes a stale hook script and its registrations on refresh", () => {
|
|
109
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
110
|
-
const hookDir = join(consumer, ".agents", "hooks", "imf-web-ui");
|
|
111
|
-
writeFileSync(join(hookDir, "retired-hook.sh"), "#!/usr/bin/env sh\nexit 0\n");
|
|
112
|
-
const settingsFile = join(consumer, ".claude", "settings.json");
|
|
113
|
-
const settings = readSettings(consumer);
|
|
114
|
-
settings.hooks.PostToolUse = [
|
|
115
|
-
{
|
|
116
|
-
hooks: [{ type: "command", command: '"$CLAUDE_PROJECT_DIR/.agents/hooks/imf-web-ui/retired-hook.sh"' }]
|
|
117
|
-
} as never
|
|
118
|
-
];
|
|
119
|
-
writeFileSync(settingsFile, JSON.stringify(settings));
|
|
120
|
-
|
|
121
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
122
|
-
|
|
123
|
-
expect(existsSync(join(hookDir, "retired-hook.sh"))).toBe(false);
|
|
124
|
-
const refreshed = readSettings(consumer);
|
|
125
|
-
expect(refreshed.hooks.PostToolUse).toBeUndefined();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("prunes per hook, keeping consumer hooks and customized commands in the same entry", () => {
|
|
129
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
130
|
-
const hookDir = join(consumer, ".agents", "hooks", "imf-web-ui");
|
|
131
|
-
writeFileSync(join(hookDir, "retired-hook.sh"), "#!/usr/bin/env sh\nexit 0\n");
|
|
132
|
-
const settingsFile = join(consumer, ".claude", "settings.json");
|
|
133
|
-
const settings = readSettings(consumer);
|
|
134
|
-
const customized = '"$CLAUDE_PROJECT_DIR/.agents/hooks/imf-web-ui/session-start.sh" 2>/dev/null';
|
|
135
|
-
settings.hooks.PostToolUse = [
|
|
136
|
-
{
|
|
137
|
-
hooks: [
|
|
138
|
-
{ type: "command", command: '"$CLAUDE_PROJECT_DIR/.agents/hooks/imf-web-ui/retired-hook.sh"' },
|
|
139
|
-
{ type: "command", command: "npm run my-check" },
|
|
140
|
-
{ type: "command", command: customized }
|
|
141
|
-
]
|
|
142
|
-
} as never
|
|
143
|
-
];
|
|
144
|
-
writeFileSync(settingsFile, JSON.stringify(settings));
|
|
145
|
-
|
|
146
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
147
|
-
|
|
148
|
-
const commands = readSettings(consumer).hooks.PostToolUse.flatMap(entry => entry.hooks.map(hook => hook.command));
|
|
149
|
-
expect(commands).toEqual(["npm run my-check", customized]);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("keeps a consumer-adjusted codex command across re-runs, without duplicating it", () => {
|
|
153
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
154
|
-
const codexPath = join(consumer, ".codex", "hooks.json");
|
|
155
|
-
const adjusted = '"$(git rev-parse --show-toplevel)/frontend/.agents/hooks/imf-web-ui/session-start.sh"';
|
|
156
|
-
const codexHooks = JSON.parse(readFileSync(codexPath, "utf-8")) as {
|
|
157
|
-
hooks: Record<string, { hooks: { command?: string }[] }[]>;
|
|
158
|
-
};
|
|
159
|
-
codexHooks.hooks.SessionStart[0].hooks[0].command = adjusted;
|
|
160
|
-
writeFileSync(codexPath, JSON.stringify(codexHooks));
|
|
161
|
-
|
|
162
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
163
|
-
|
|
164
|
-
const refreshed = JSON.parse(readFileSync(codexPath, "utf-8")) as {
|
|
165
|
-
hooks: Record<string, { hooks: { command?: string }[] }[]>;
|
|
166
|
-
};
|
|
167
|
-
const commands = refreshed.hooks.SessionStart.flatMap(entry => entry.hooks.map(hook => hook.command));
|
|
168
|
-
expect(commands).toEqual([adjusted]);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("merges registrations without clobbering existing settings, idempotently", () => {
|
|
172
|
-
mkdirSync(join(consumer, ".claude"), { recursive: true });
|
|
173
|
-
writeFileSync(
|
|
174
|
-
join(consumer, ".claude", "settings.json"),
|
|
175
|
-
JSON.stringify({
|
|
176
|
-
permissions: { allow: ["Bash(git *)"] },
|
|
177
|
-
hooks: { PostToolUse: [{ matcher: "Edit", hooks: [{ type: "command", command: "echo existing" }] }] }
|
|
178
|
-
})
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
182
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
183
|
-
|
|
184
|
-
const settings = readSettings(consumer);
|
|
185
|
-
expect(settings.permissions?.allow).toEqual(["Bash(git *)"]);
|
|
186
|
-
const foreign = settings.hooks.PostToolUse.flatMap(entry => entry.hooks.map(hook => hook.command));
|
|
187
|
-
expect(foreign).toContain("echo existing");
|
|
188
|
-
// the shipped registration is added exactly once across both runs
|
|
189
|
-
const commands = settings.hooks.UserPromptSubmit.flatMap(entry => entry.hooks.map(hook => hook.command));
|
|
190
|
-
expect(commands.filter(command => command?.includes("user-prompt-submit.sh"))).toHaveLength(1);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("recognises registrations written with braced ${CLAUDE_PROJECT_DIR}", () => {
|
|
194
|
-
mkdirSync(join(consumer, ".claude"), { recursive: true });
|
|
195
|
-
writeFileSync(
|
|
196
|
-
join(consumer, ".claude", "settings.json"),
|
|
197
|
-
JSON.stringify({
|
|
198
|
-
hooks: {
|
|
199
|
-
UserPromptSubmit: [
|
|
200
|
-
{
|
|
201
|
-
hooks: [
|
|
202
|
-
{
|
|
203
|
-
type: "command",
|
|
204
|
-
command: '"${CLAUDE_PROJECT_DIR}/.agents/hooks/imf-web-ui/user-prompt-submit.sh"'
|
|
205
|
-
}
|
|
206
|
-
]
|
|
207
|
-
},
|
|
208
|
-
// a foreign entry carrying no `command` at all must not throw
|
|
209
|
-
{ matcher: "Bash", hooks: [{ type: "command", args: ["${CLAUDE_PROJECT_DIR}/other/gate.mjs"] }] }
|
|
210
|
-
]
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
216
|
-
|
|
217
|
-
const settings = readSettings(consumer);
|
|
218
|
-
const commands = settings.hooks.UserPromptSubmit.flatMap(entry => entry.hooks.map(hook => hook.command));
|
|
219
|
-
expect(commands.filter(command => command?.includes("user-prompt-submit.sh"))).toHaveLength(1);
|
|
220
|
-
expect(commands).toContain('"${CLAUDE_PROJECT_DIR}/.agents/hooks/imf-web-ui/user-prompt-submit.sh"');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it("does not touch settings.json when every registration is already present", () => {
|
|
224
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
225
|
-
const settingsFile = join(consumer, ".claude", "settings.json");
|
|
226
|
-
const settings = readSettings(consumer);
|
|
227
|
-
settings.hooks.PreToolUse = [{ hooks: [{ command: "./other/gate.mjs" }] }];
|
|
228
|
-
const before = JSON.stringify(settings, null, 4) + "\n";
|
|
229
|
-
writeFileSync(settingsFile, before);
|
|
230
|
-
|
|
231
|
-
const result = runInstall(consumer, "--hooks", "--target", "agents");
|
|
232
|
-
|
|
233
|
-
expect(readFileSync(settingsFile, "utf-8")).toBe(before);
|
|
234
|
-
expect(result.stdout).toContain("Hook registrations already present");
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it("upserts the fenced AGENTS.md block and leaves the rest untouched", () => {
|
|
238
|
-
const agentsPath = join(consumer, "AGENTS.md");
|
|
239
|
-
writeFileSync(
|
|
240
|
-
agentsPath,
|
|
241
|
-
"# AGENTS.md\n\nRepo-specific intro.\n\n<!-- imf-web-ui:begin stale -->\nold content\n<!-- imf-web-ui:end -->\n\nRepo-specific outro.\n"
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
runInstall(consumer, "--skills", "--target", "agents");
|
|
245
|
-
const updated = readFileSync(agentsPath, "utf-8");
|
|
246
|
-
expect(updated).toContain("Repo-specific intro.");
|
|
247
|
-
expect(updated).toContain("Repo-specific outro.");
|
|
248
|
-
expect(updated).not.toContain("old content");
|
|
249
|
-
expect(updated).toContain("resynced with `npx web-ui-install`");
|
|
250
|
-
|
|
251
|
-
// no fence yet → block is appended; missing AGENTS.md entirely → left alone
|
|
252
|
-
writeFileSync(agentsPath, "# AGENTS.md\n\nNo fence here.\n");
|
|
253
|
-
runInstall(consumer, "--skills", "--target", "agents");
|
|
254
|
-
const appended = readFileSync(agentsPath, "utf-8");
|
|
255
|
-
expect(appended).toContain("No fence here.");
|
|
256
|
-
expect(appended).toContain("<!-- imf-web-ui:begin");
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it("ships injection hooks that exit 0 and echo their fixed one-liners", () => {
|
|
260
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
261
|
-
const hookDir = join(consumer, ".agents", "hooks", "imf-web-ui");
|
|
262
|
-
|
|
263
|
-
const sessionStart = spawnSync("sh", [join(hookDir, "session-start.sh")], { encoding: "utf-8" });
|
|
264
|
-
expect(sessionStart.status).toBe(0);
|
|
265
|
-
expect(sessionStart.stdout).toContain("imf-web-ui skill");
|
|
266
|
-
|
|
267
|
-
// Subagents get the same router pointer, byte for byte.
|
|
268
|
-
const subagentStart = spawnSync("sh", [join(hookDir, "subagent-start.sh")], { encoding: "utf-8" });
|
|
269
|
-
expect(subagentStart.status).toBe(0);
|
|
270
|
-
expect(subagentStart.stdout).toBe(sessionStart.stdout);
|
|
271
|
-
|
|
272
|
-
const promptSubmit = spawnSync("sh", [join(hookDir, "user-prompt-submit.sh")], { encoding: "utf-8" });
|
|
273
|
-
expect(promptSubmit.status).toBe(0);
|
|
274
|
-
expect(promptSubmit.stdout).toContain("imf-web-ui-components");
|
|
275
|
-
|
|
276
|
-
// no node_modules in the consumer → silent success
|
|
277
|
-
const staleness = spawnSync("sh", [join(hookDir, "baseline-staleness.sh")], { cwd: consumer, encoding: "utf-8" });
|
|
278
|
-
expect(staleness.status).toBe(0);
|
|
279
|
-
expect(staleness.stdout).toBe("");
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it("gates the turn end only after in-session edits, and never loops", () => {
|
|
283
|
-
runInstall(consumer, "--hooks", "--target", "agents");
|
|
284
|
-
const stopHook = join(consumer, ".agents", "hooks", "imf-web-ui", "stop.sh");
|
|
285
|
-
const transcript = join(consumer, "transcript.jsonl");
|
|
286
|
-
const runStop = (input: string) => spawnSync("sh", [stopHook], { cwd: consumer, input, encoding: "utf-8" });
|
|
287
|
-
|
|
288
|
-
// outside a git repo → silent pass
|
|
289
|
-
const noGit = runStop("{}");
|
|
290
|
-
expect(noGit.status).toBe(0);
|
|
291
|
-
expect(noGit.stdout).toBe("");
|
|
292
|
-
|
|
293
|
-
spawnSync("git", ["init", "--quiet", consumer]);
|
|
294
|
-
mkdirSync(join(consumer, "src"), { recursive: true });
|
|
295
|
-
writeFileSync(join(consumer, "src", "app.ts"), "export const x = 1;\n");
|
|
296
|
-
writeFileSync(transcript, '{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Write"}]}}\n');
|
|
297
|
-
const payload = JSON.stringify({ transcript_path: transcript, stop_hook_active: false });
|
|
298
|
-
|
|
299
|
-
const blocked = runStop(payload);
|
|
300
|
-
expect(blocked.status).toBe(0);
|
|
301
|
-
expect(blocked.stdout).toContain('"decision":"block"');
|
|
302
|
-
expect(blocked.stdout).toContain("web-ui stop gate:");
|
|
303
|
-
|
|
304
|
-
// re-entry after a block → silent pass
|
|
305
|
-
const reentry = runStop(JSON.stringify({ transcript_path: transcript, stop_hook_active: true }));
|
|
306
|
-
expect(reentry.status).toBe(0);
|
|
307
|
-
expect(reentry.stdout).toBe("");
|
|
308
|
-
|
|
309
|
-
// dirty tree, but this session's transcript shows no edits → silent pass
|
|
310
|
-
writeFileSync(transcript, '{"type":"user","message":{"content":"a question"}}\n');
|
|
311
|
-
const questionTurn = runStop(payload);
|
|
312
|
-
expect(questionTurn.status).toBe(0);
|
|
313
|
-
expect(questionTurn.stdout).toBe("");
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it("warns from the staleness script when skill markers lag the package", () => {
|
|
317
|
-
runInstall(consumer, "--skills", "--hooks", "--target", "agents");
|
|
318
|
-
const pkgDir = join(consumer, "node_modules", "@imfusion", "web-ui");
|
|
319
|
-
mkdirSync(pkgDir, { recursive: true });
|
|
320
|
-
writeFileSync(join(pkgDir, "package.json"), JSON.stringify({ version: "99.0.0" }));
|
|
321
|
-
|
|
322
|
-
const result = spawnSync("sh", [join(consumer, ".agents", "hooks", "imf-web-ui", "baseline-staleness.sh")], {
|
|
323
|
-
cwd: consumer,
|
|
324
|
-
encoding: "utf-8"
|
|
325
|
-
});
|
|
326
|
-
expect(result.status).toBe(0);
|
|
327
|
-
expect(result.stdout).toContain("npx web-ui-install");
|
|
328
|
-
});
|
|
329
|
-
});
|
package/dist/code-Blo48PGr.js
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./chunk-DmhlhrBa.js";
|
|
2
|
-
import { t } from "./meta-CySnRuVp.js";
|
|
3
|
-
import { Ma as n, Qt as r, Xo as i, hn as a, r as o } from "./icons-wBmF0U2x.js";
|
|
4
|
-
import { cx as s } from "class-variance-authority";
|
|
5
|
-
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
6
|
-
import { useCallback as u, useEffect as d, useRef as f, useState as p } from "react";
|
|
7
|
-
//#region src/hooks/use-clipboard.ts
|
|
8
|
-
function m({ timeout: e = 1500 } = {}) {
|
|
9
|
-
let [t, n] = p(!1), r = f(void 0);
|
|
10
|
-
return d(() => () => clearTimeout(r.current), []), {
|
|
11
|
-
copied: t,
|
|
12
|
-
copy: u((t) => {
|
|
13
|
-
navigator.clipboard.writeText(t).then(() => {
|
|
14
|
-
n(!0), clearTimeout(r.current), r.current = setTimeout(() => n(!1), e);
|
|
15
|
-
});
|
|
16
|
-
}, [e])
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
var h = {
|
|
20
|
-
block: "imf-ui-code-module-block",
|
|
21
|
-
copy: "imf-ui-code-module-copy",
|
|
22
|
-
icon: "imf-ui-code-module-icon",
|
|
23
|
-
inline: "imf-ui-code-module-inline",
|
|
24
|
-
inlineCopy: "imf-ui-code-module-inlineCopy",
|
|
25
|
-
inlineLink: "imf-ui-code-module-inlineLink",
|
|
26
|
-
language: "imf-ui-code-module-language",
|
|
27
|
-
pre: "imf-ui-code-module-pre",
|
|
28
|
-
separator: "imf-ui-code-module-separator"
|
|
29
|
-
}, g = /* @__PURE__ */ e({
|
|
30
|
-
Block: () => y,
|
|
31
|
-
Inline: () => _,
|
|
32
|
-
InlineLink: () => v
|
|
33
|
-
});
|
|
34
|
-
function _({ className: e, copyable: t = !1, children: r, ...a }) {
|
|
35
|
-
let { copied: u, copy: d } = m(), p = f(null), g = u ? n : i;
|
|
36
|
-
return /* @__PURE__ */ l("code", {
|
|
37
|
-
...a,
|
|
38
|
-
"data-imf-ui-component": "Code.Inline",
|
|
39
|
-
className: s(h.inline, e),
|
|
40
|
-
children: [
|
|
41
|
-
/* @__PURE__ */ c("span", {
|
|
42
|
-
ref: p,
|
|
43
|
-
children: r
|
|
44
|
-
}),
|
|
45
|
-
t && /* @__PURE__ */ c("span", { className: h.separator }),
|
|
46
|
-
t && /* @__PURE__ */ c("button", {
|
|
47
|
-
type: "button",
|
|
48
|
-
className: h.inlineCopy,
|
|
49
|
-
"data-copied": u || void 0,
|
|
50
|
-
"aria-label": u ? "Copied" : "Copy",
|
|
51
|
-
onClick: () => d(p.current?.textContent ?? ""),
|
|
52
|
-
children: /* @__PURE__ */ c(o, {
|
|
53
|
-
glyph: g,
|
|
54
|
-
size: 12,
|
|
55
|
-
strokeWidth: 2,
|
|
56
|
-
"aria-hidden": !0
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
]
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
_.displayName = "Code.Inline";
|
|
63
|
-
function v({ className: e, kind: t = "external", children: n, ...i }) {
|
|
64
|
-
let u = t === "external" ? a : r;
|
|
65
|
-
return /* @__PURE__ */ l("a", {
|
|
66
|
-
...t === "external" ? {
|
|
67
|
-
target: "_blank",
|
|
68
|
-
rel: "noreferrer"
|
|
69
|
-
} : {},
|
|
70
|
-
...i,
|
|
71
|
-
"data-imf-ui-component": "Code.InlineLink",
|
|
72
|
-
"data-kind": t,
|
|
73
|
-
className: s(h.inline, h.inlineLink, e),
|
|
74
|
-
children: [
|
|
75
|
-
n,
|
|
76
|
-
/* @__PURE__ */ c("span", { className: h.separator }),
|
|
77
|
-
/* @__PURE__ */ c("span", {
|
|
78
|
-
className: h.icon,
|
|
79
|
-
"aria-hidden": !0,
|
|
80
|
-
children: /* @__PURE__ */ c(o, {
|
|
81
|
-
glyph: u,
|
|
82
|
-
size: 12,
|
|
83
|
-
strokeWidth: 2
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
]
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
v.displayName = "Code.InlineLink";
|
|
90
|
-
function y({ className: e, language: t, hideCopy: r = !1, children: a, ...u }) {
|
|
91
|
-
let { copied: d, copy: p } = m(), g = f(null), _ = d ? n : i;
|
|
92
|
-
return /* @__PURE__ */ l("div", {
|
|
93
|
-
"data-imf-ui-component": "Code.Block",
|
|
94
|
-
className: s(h.block, e),
|
|
95
|
-
children: [
|
|
96
|
-
t != null && /* @__PURE__ */ c("span", {
|
|
97
|
-
className: h.language,
|
|
98
|
-
children: t
|
|
99
|
-
}),
|
|
100
|
-
!r && /* @__PURE__ */ c("button", {
|
|
101
|
-
type: "button",
|
|
102
|
-
className: h.copy,
|
|
103
|
-
"data-copied": d || void 0,
|
|
104
|
-
"aria-label": d ? "Copied" : "Copy",
|
|
105
|
-
onClick: () => p(g.current?.textContent ?? ""),
|
|
106
|
-
children: /* @__PURE__ */ c(o, {
|
|
107
|
-
glyph: _,
|
|
108
|
-
size: 12,
|
|
109
|
-
strokeWidth: 2,
|
|
110
|
-
"aria-hidden": !0
|
|
111
|
-
})
|
|
112
|
-
}),
|
|
113
|
-
/* @__PURE__ */ c("pre", {
|
|
114
|
-
...u,
|
|
115
|
-
className: h.pre,
|
|
116
|
-
children: /* @__PURE__ */ c("code", {
|
|
117
|
-
ref: g,
|
|
118
|
-
children: a
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
]
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
y.displayName = "Code.Block";
|
|
125
|
-
//#endregion
|
|
126
|
-
//#region src/components/code/code.meta.ts
|
|
127
|
-
var b = {
|
|
128
|
-
name: "Code",
|
|
129
|
-
category: t.display,
|
|
130
|
-
status: "stable",
|
|
131
|
-
namespace: !0,
|
|
132
|
-
description: "Displays source code — Code.Inline for a fragment in running text, Code.Block for a fenced block with an optional language label and copy button. Also called a code snippet or code block.",
|
|
133
|
-
origin: "native"
|
|
134
|
-
};
|
|
135
|
-
//#endregion
|
|
136
|
-
export { g as a, v as i, y as n, m as o, _ as r, b as t };
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { IconConfig } from './icon-context';
|
|
3
|
-
interface IconConfigProviderProps {
|
|
4
|
-
children: ReactNode;
|
|
5
|
-
value?: IconConfig;
|
|
6
|
-
}
|
|
7
|
-
export declare function IconConfigProvider({ children, value }: IconConfigProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export {};
|