create-druid-ui 2.1.2 → 2.1.4

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/bin/create.js CHANGED
@@ -2,8 +2,10 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { downloadTemplate } from "giget";
5
- import { resolve } from "node:path";
5
+ import { copyFile } from "node:fs/promises";
6
+ import { dirname, join, resolve } from "node:path";
6
7
  import { execSync } from "node:child_process";
8
+ import { fileURLToPath } from "node:url";
7
9
  var args = process.argv.slice(2);
8
10
  var projectName = args.find((arg) => !arg.startsWith("-"));
9
11
  var templateFlag = args.indexOf("-t") !== -1 ? args.indexOf("-t") : args.indexOf("--template");
@@ -16,12 +18,15 @@ if (!projectName) {
16
18
  process.exit(1);
17
19
  }
18
20
  var projectDir = resolve(projectName);
21
+ var packageDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
22
+ var aiGuidePath = join(packageDir, "src", "AGENTS.md");
19
23
  async function main() {
20
24
  console.log(`Creating "${projectName}" with template "${template}"...`);
21
25
  await downloadTemplate(`gh:highcard-dev/druid-ui/examples/${template}`, {
22
26
  dir: projectDir,
23
27
  force: false
24
28
  });
29
+ await copyFile(aiGuidePath, join(projectDir, "AGENTS.md"));
25
30
  console.log("Installing dependencies...");
26
31
  try {
27
32
  execSync("npm install", { cwd: projectDir, stdio: "inherit" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-druid-ui",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-druid-ui": "./bin/create.js"
package/src/AGENTS.md ADDED
@@ -0,0 +1,175 @@
1
+ # Druid UI Guide For AI Assistants
2
+
3
+ This is a Druid UI project. Druid UI is a WebAssembly-first UI framework for hosting user-provided components safely. It uses JSX/TSX for authoring, but it is not React.
4
+
5
+ ## Core Concepts
6
+
7
+ - Component code runs as a Druid UI guest component.
8
+ - Host code creates and configures a `DruidUI` custom element from `@druid-ui/host`.
9
+ - JSX is compiled through `@druid-ui/component`; do not add React imports, React hooks, or React DOM APIs.
10
+ - A component exports `component = createComponent(...)` from `@druid-ui/component`.
11
+ - Component render functions return JSX or a Druid UI node id.
12
+ - Component state is usually stored in module-level variables.
13
+ - Every event handler execution ends with a rerender. Druid UI rerenders the whole component tree rather than using React-style state reconciliation.
14
+ - Sandboxed components cannot use arbitrary browser APIs. Host capabilities must be explicitly exposed through Druid UI extensions.
15
+
16
+ ## Creating A Component
17
+
18
+ Create a TSX file and export a Druid UI component:
19
+
20
+ ```tsx
21
+ import { type Context, type Event, createComponent } from "@druid-ui/component";
22
+
23
+ let count = 0;
24
+
25
+ export const component = createComponent((ctx: Context) => {
26
+ return (
27
+ <main>
28
+ <h1>Hello from Druid UI</h1>
29
+ <p>Path: {ctx.path}</p>
30
+ <button
31
+ onClick={(event: Event) => {
32
+ count++;
33
+ }}
34
+ >
35
+ Clicked {count} times
36
+ </button>
37
+ </main>
38
+ );
39
+ });
40
+ ```
41
+
42
+ Important rules:
43
+
44
+ - Import `createComponent`, `Context`, `Event`, and `log` from `@druid-ui/component`.
45
+ - Do not import from `react`.
46
+ - Do not use `useState`, `useEffect`, `useMemo`, or other React hooks.
47
+ - Store simple mutable UI state in module-level variables.
48
+ - Event handlers may mutate module-level state; Druid UI rerenders after the handler runs.
49
+ - Keep browser-only APIs out of sandboxed component code unless a host extension provides them.
50
+
51
+ ## Creating And Using Child Components
52
+
53
+ Reusable components are plain functions that return JSX. Use them like JSX tags inside the root `createComponent` render function.
54
+
55
+ ```tsx
56
+ import { type Context, createComponent } from "@druid-ui/component";
57
+
58
+ type CardProps = {
59
+ title: string;
60
+ children?: string | JSX.Element | Array<string | JSX.Element>;
61
+ };
62
+
63
+ const Card = ({ title, children }: CardProps) => {
64
+ return (
65
+ <section>
66
+ <h2>{title}</h2>
67
+ <div>{children}</div>
68
+ </section>
69
+ );
70
+ };
71
+
72
+ const Greeting = ({ name }: { name: string }) => {
73
+ return <p>Hello {name}</p>;
74
+ };
75
+
76
+ export const component = createComponent((ctx: Context) => {
77
+ return (
78
+ <main>
79
+ <Card title="User">
80
+ <Greeting name="Druid UI" />
81
+ </Card>
82
+ </main>
83
+ );
84
+ });
85
+ ```
86
+
87
+ Child component rules:
88
+
89
+ - Define reusable UI as plain functions, not React components with hooks.
90
+ - Pass data through props like normal JSX.
91
+ - Keep shared mutable state at module scope or pass values down as props.
92
+ - Event handlers can be passed as props and attached to DOM elements inside child components.
93
+ - Child components should return one JSX node or string-like render output.
94
+ - Only the exported `component = createComponent(...)` is the Druid UI entry point; child components are implementation details.
95
+
96
+ ## Hosting A Component
97
+
98
+ In a full app template, `src/main.ts` creates the host element and points it at the built component artifact:
99
+
100
+ ```ts
101
+ import { DruidUI } from "@druid-ui/host";
102
+ import { ViteHMR } from "@druid-ui/vite/client";
103
+
104
+ const druidUiElement = new DruidUI();
105
+
106
+ if (import.meta.env.DEV) {
107
+ druidUiElement.sandbox = false;
108
+ druidUiElement.setAttribute("entrypoint", "/app.bundled-raw.js");
109
+ } else {
110
+ druidUiElement.setAttribute("entrypoint", "/app.wasm");
111
+ }
112
+
113
+ document.getElementById("app")?.appendChild(druidUiElement);
114
+ ViteHMR(druidUiElement);
115
+ ```
116
+
117
+ Modes:
118
+
119
+ - Sandbox mode loads a `.wasm` component and isolates guest code.
120
+ - No-sandbox mode loads a raw JavaScript bundle and is faster for local development.
121
+ - Prefer no-sandbox mode while editing, then validate behavior in sandbox mode before production.
122
+
123
+ ## Build Commands
124
+
125
+ Use the scripts in `package.json`; common commands are:
126
+
127
+ - `npm run dev` starts local development.
128
+ - `npm run build` builds the sandboxed WASM artifact.
129
+ - `npm run build:raw` builds the raw JavaScript artifact when available.
130
+
131
+ Direct build examples:
132
+
133
+ ```bash
134
+ druid-ui-build src/component/app.tsx dist
135
+ druid-ui-build src/component/app.tsx dist --raw
136
+ ```
137
+
138
+ ## Extensions And Host APIs
139
+
140
+ Druid UI components only receive APIs the host explicitly provides. Add host APIs with WIT and `extensionObject`.
141
+
142
+ Host side:
143
+
144
+ ```ts
145
+ import { DruidUI, PromiseToResult } from "@druid-ui/host";
146
+
147
+ const druidUiElement = new DruidUI();
148
+
149
+ druidUiElement.extensionObject = {
150
+ "druid:ui/extension": {
151
+ requestGet: PromiseToResult(async (url: string) => {
152
+ const res = await fetch(url);
153
+ return res.text();
154
+ }),
155
+ },
156
+ };
157
+ ```
158
+
159
+ Component side:
160
+
161
+ - Define imported functions in a `.wit` file.
162
+ - Generate or use TypeScript wrappers for those imports.
163
+ - Await async host APIs from event handlers or helper functions.
164
+ - Keep WIT names, TypeScript wrapper names, and `extensionObject` keys in sync.
165
+
166
+ Platform examples use the `"druid:ui/plattform"` extension and `@druid-ui/plattform` helpers such as `request`, `loadFileFromDeployment`, and `saveFileToDeployment`.
167
+
168
+ ## Common Pitfalls
169
+
170
+ - Do not treat TSX as React. JSX here creates Druid UI nodes.
171
+ - Do not use React component lifecycle patterns.
172
+ - Do not put secret or privileged logic into guest component code.
173
+ - Do not call browser APIs from sandboxed components unless exposed by the host.
174
+ - Do not forget to keep WIT files and generated TypeScript types in sync after changing extension APIs.
175
+ - Do not assume no-sandbox behavior is equivalent to sandbox behavior for security or host API access.
package/src/index.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { downloadTemplate } from "giget";
4
- import { resolve } from "node:path";
4
+ import { copyFile } from "node:fs/promises";
5
+ import { dirname, join, resolve } from "node:path";
5
6
  import { execSync } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
6
8
 
7
9
  const args = process.argv.slice(2);
8
10
  const projectName = args.find((arg) => !arg.startsWith("-"));
@@ -19,6 +21,8 @@ if (!projectName) {
19
21
  }
20
22
 
21
23
  const projectDir = resolve(projectName);
24
+ const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
25
+ const aiGuidePath = join(packageDir, "src", "AGENTS.md");
22
26
 
23
27
  async function main() {
24
28
  console.log(`Creating "${projectName}" with template "${template}"...`);
@@ -28,6 +32,8 @@ async function main() {
28
32
  force: false,
29
33
  });
30
34
 
35
+ await copyFile(aiGuidePath, join(projectDir, "AGENTS.md"));
36
+
31
37
  console.log("Installing dependencies...");
32
38
 
33
39
  try {