@imfusion/web-ui 0.5.0 → 0.5.1-dev.11.g2e949f6d
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 +175 -40
- package/bin/install.js +319 -0
- package/bin/install.test.ts +139 -0
- package/dist/components/logo/logo.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/style.css +1 -1
- package/package.json +30 -22
- package/src/docgen/doc.gen.json +1 -1
- package/src/llms/skills/imf-web-ui/SKILL.md +17 -8
- package/src/llms/skills/imf-web-ui-agent-setup/SKILL.md +46 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/baseline-staleness.sh +17 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/post-tool-use.sh +21 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/session-start.sh +4 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/user-prompt-submit.sh +4 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/settings.json +37 -0
- package/src/llms/skills/imf-web-ui-components/SKILL.md +1 -1
- package/src/llms/skills/imf-web-ui-frontend-conventions/SKILL.md +44 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/assets.md +23 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/components.md +63 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/data.md +130 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/docs-structure.md +44 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/git.md +33 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/library-boundary.md +37 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/npm-project.md +57 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/project-structure.md +21 -0
- package/src/llms/skills/{imf-web-ui-frontend-patterns/references/react-patterns.md → imf-web-ui-frontend-conventions/references/react.md} +26 -12
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/stack.md +39 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/styling.md +91 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/testing.md +16 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/tooling.md +65 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/typescript.md +45 -0
- package/src/llms/skills/imf-web-ui-frontend-setup/SKILL.md +83 -0
- package/src/llms/skills/imf-web-ui-frontend-setup/templates/AGENTS.md +34 -0
- package/src/llms/skills/imf-web-ui-frontend-setup/templates/README.md +27 -0
- package/src/llms/skills/imf-web-ui-library-setup/SKILL.md +56 -0
- package/src/llms/skills/imf-web-ui-ux/SKILL.md +4 -4
- package/src/llms/skills/imf-web-ui-ux/references/forms.md +4 -2
- package/bin/install-skill.js +0 -180
- package/src/llms/skills/imf-web-ui-frontend-patterns/SKILL.md +0 -67
- package/src/llms/skills/imf-web-ui-setup/SKILL.md +0 -30
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const installer = resolve(fileURLToPath(new URL(".", import.meta.url)), "install.js");
|
|
9
|
+
|
|
10
|
+
function runInstall(cwd: string, ...args: string[]) {
|
|
11
|
+
const result = spawnSync("node", [installer, ...args], { cwd, encoding: "utf-8" });
|
|
12
|
+
expect(result.status).toBe(0);
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readSettings(cwd: string) {
|
|
17
|
+
return JSON.parse(readFileSync(join(cwd, ".claude", "settings.json"), "utf-8")) as {
|
|
18
|
+
permissions?: { allow: string[] };
|
|
19
|
+
hooks: Record<string, { hooks: { command: string }[] }[]>;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let consumer: string;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
consumer = mkdtempSync(join(tmpdir(), "web-ui-install-"));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("web-ui-install", () => {
|
|
30
|
+
it("installs skills into both targets with version markers", () => {
|
|
31
|
+
runInstall(consumer, "--skills", "--target", "agents", "--target", "claude");
|
|
32
|
+
|
|
33
|
+
const installed = readdirSync(join(consumer, ".agents", "skills"));
|
|
34
|
+
expect(installed).toContain("imf-web-ui");
|
|
35
|
+
expect(installed).toContain("imf-web-ui-frontend-conventions");
|
|
36
|
+
expect(installed).toContain("imf-web-ui-frontend-setup");
|
|
37
|
+
|
|
38
|
+
for (const name of installed) {
|
|
39
|
+
expect(existsSync(join(consumer, ".agents", "skills", name, ".imf-web-ui-skill-version.json"))).toBe(true);
|
|
40
|
+
expect(lstatSync(join(consumer, ".claude", "skills", name)).isSymbolicLink()).toBe(true);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("installs hooks into an installer-owned directory and registers them", () => {
|
|
45
|
+
runInstall(consumer, "--hooks");
|
|
46
|
+
|
|
47
|
+
const hookDir = join(consumer, ".agents", "hooks", "imf-web-ui");
|
|
48
|
+
const scripts = readdirSync(hookDir);
|
|
49
|
+
expect(scripts).toEqual(expect.arrayContaining(["session-start.sh", "user-prompt-submit.sh", "post-tool-use.sh"]));
|
|
50
|
+
for (const script of scripts) {
|
|
51
|
+
expect(statSync(join(hookDir, script)).mode & 0o100).toBeTruthy();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const settings = readSettings(consumer);
|
|
55
|
+
expect(Object.keys(settings.hooks)).toEqual(expect.arrayContaining(["SessionStart", "UserPromptSubmit", "PostToolUse"]));
|
|
56
|
+
// --hooks alone installs no skills
|
|
57
|
+
expect(existsSync(join(consumer, ".agents", "skills"))).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("merges registrations without clobbering existing settings, idempotently", () => {
|
|
61
|
+
mkdirSync(join(consumer, ".claude"), { recursive: true });
|
|
62
|
+
writeFileSync(
|
|
63
|
+
join(consumer, ".claude", "settings.json"),
|
|
64
|
+
JSON.stringify({
|
|
65
|
+
permissions: { allow: ["Bash(git *)"] },
|
|
66
|
+
hooks: { PostToolUse: [{ matcher: "Edit", hooks: [{ type: "command", command: "echo existing" }] }] }
|
|
67
|
+
})
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
runInstall(consumer, "--hooks");
|
|
71
|
+
runInstall(consumer, "--hooks");
|
|
72
|
+
|
|
73
|
+
const settings = readSettings(consumer);
|
|
74
|
+
expect(settings.permissions?.allow).toEqual(["Bash(git *)"]);
|
|
75
|
+
const commands = settings.hooks.PostToolUse.flatMap(entry => entry.hooks.map(hook => hook.command));
|
|
76
|
+
expect(commands).toContain("echo existing");
|
|
77
|
+
// the shipped registration is added exactly once across both runs
|
|
78
|
+
expect(commands.filter(command => command.includes("post-tool-use.sh"))).toHaveLength(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("upserts the fenced AGENTS.md block and leaves the rest untouched", () => {
|
|
82
|
+
const agentsPath = join(consumer, "AGENTS.md");
|
|
83
|
+
writeFileSync(
|
|
84
|
+
agentsPath,
|
|
85
|
+
"# 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"
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
runInstall(consumer, "--skills", "--target", "agents");
|
|
89
|
+
const updated = readFileSync(agentsPath, "utf-8");
|
|
90
|
+
expect(updated).toContain("Repo-specific intro.");
|
|
91
|
+
expect(updated).toContain("Repo-specific outro.");
|
|
92
|
+
expect(updated).not.toContain("old content");
|
|
93
|
+
expect(updated).toContain("resynced with `npx web-ui-install`");
|
|
94
|
+
|
|
95
|
+
// no fence yet → block is appended; missing AGENTS.md entirely → left alone
|
|
96
|
+
writeFileSync(agentsPath, "# AGENTS.md\n\nNo fence here.\n");
|
|
97
|
+
runInstall(consumer, "--skills", "--target", "agents");
|
|
98
|
+
const appended = readFileSync(agentsPath, "utf-8");
|
|
99
|
+
expect(appended).toContain("No fence here.");
|
|
100
|
+
expect(appended).toContain("<!-- imf-web-ui:begin");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("ships hooks that stay advisory: exit 0 with and without a usable payload", () => {
|
|
104
|
+
runInstall(consumer, "--hooks");
|
|
105
|
+
const hookDir = join(consumer, ".agents", "hooks", "imf-web-ui");
|
|
106
|
+
|
|
107
|
+
const noFile = spawnSync("sh", [join(hookDir, "post-tool-use.sh")], { input: "{}", encoding: "utf-8" });
|
|
108
|
+
expect(noFile.status).toBe(0);
|
|
109
|
+
|
|
110
|
+
const missingFile = spawnSync("sh", [join(hookDir, "post-tool-use.sh")], {
|
|
111
|
+
input: JSON.stringify({ tool_input: { file_path: join(consumer, "nope.ts") } }),
|
|
112
|
+
encoding: "utf-8"
|
|
113
|
+
});
|
|
114
|
+
expect(missingFile.status).toBe(0);
|
|
115
|
+
|
|
116
|
+
const sessionStart = spawnSync("sh", [join(hookDir, "session-start.sh")], { encoding: "utf-8" });
|
|
117
|
+
expect(sessionStart.status).toBe(0);
|
|
118
|
+
expect(sessionStart.stdout).toContain("imf-web-ui-frontend-conventions");
|
|
119
|
+
|
|
120
|
+
// no node_modules in the consumer → silent success
|
|
121
|
+
const staleness = spawnSync("sh", [join(hookDir, "baseline-staleness.sh")], { cwd: consumer, encoding: "utf-8" });
|
|
122
|
+
expect(staleness.status).toBe(0);
|
|
123
|
+
expect(staleness.stdout).toBe("");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("warns from the staleness script when skill markers lag the package", () => {
|
|
127
|
+
runInstall(consumer, "--skills", "--hooks", "--target", "agents");
|
|
128
|
+
const pkgDir = join(consumer, "node_modules", "@imfusion", "web-ui");
|
|
129
|
+
mkdirSync(pkgDir, { recursive: true });
|
|
130
|
+
writeFileSync(join(pkgDir, "package.json"), JSON.stringify({ version: "99.0.0" }));
|
|
131
|
+
|
|
132
|
+
const result = spawnSync("sh", [join(consumer, ".agents", "hooks", "imf-web-ui", "baseline-staleness.sh")], {
|
|
133
|
+
cwd: consumer,
|
|
134
|
+
encoding: "utf-8"
|
|
135
|
+
});
|
|
136
|
+
expect(result.status).toBe(0);
|
|
137
|
+
expect(result.stdout).toContain("npx web-ui-install");
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -2,7 +2,7 @@ import { ReactNode } from 'react';
|
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
3
|
declare const logo: (props?: ({
|
|
4
4
|
size?: "sm" | "md" | "lg" | "hero" | "xs" | "xl" | "fit" | null | undefined;
|
|
5
|
-
variant?: "main" | "inherit" | "support" | "minor" | "brand" | null | undefined;
|
|
5
|
+
variant?: "main" | "inherit" | "support" | "minor" | "brand" | "oncolor" | null | undefined;
|
|
6
6
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
7
7
|
type Props = Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> & VariantProps<typeof logo> & {
|
|
8
8
|
/** Logo source — an image URL or an inline SVG node. Required; this primitive carries no brand default (see `ImFusionLogo` for our own mark). */
|
package/dist/index.js
CHANGED
|
@@ -10593,6 +10593,7 @@ var Xh = {
|
|
|
10593
10593
|
variantInherit: "imf-ui-logo-module-variantInherit",
|
|
10594
10594
|
variantMain: "imf-ui-logo-module-variantMain",
|
|
10595
10595
|
variantMinor: "imf-ui-logo-module-variantMinor",
|
|
10596
|
+
variantOncolor: "imf-ui-logo-module-variantOncolor",
|
|
10596
10597
|
variantSupport: "imf-ui-logo-module-variantSupport"
|
|
10597
10598
|
}, Qh = n(Zh.root, { variants: {
|
|
10598
10599
|
size: {
|
|
@@ -10609,7 +10610,8 @@ var Xh = {
|
|
|
10609
10610
|
main: Zh.variantMain,
|
|
10610
10611
|
support: Zh.variantSupport,
|
|
10611
10612
|
minor: Zh.variantMinor,
|
|
10612
|
-
brand: Zh.variantBrand
|
|
10613
|
+
brand: Zh.variantBrand,
|
|
10614
|
+
oncolor: Zh.variantOncolor
|
|
10613
10615
|
}
|
|
10614
10616
|
} });
|
|
10615
10617
|
function $h({ src: e, alt: t, size: n = "md", variant: r = "brand", height: i, className: a, style: o, ...s }) {
|