@shopify/klint 0.0.97 → 0.2.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/bin/create-editor +423 -0
- package/bin/create-sandbox +232 -0
- package/bin/klint +146 -0
- package/dist/index.cjs +2369 -317
- package/dist/index.d.cts +847 -66
- package/dist/index.d.ts +847 -66
- package/dist/index.js +2362 -316
- package/package.json +11 -17
- package/dist/elements/index.cjs +0 -946
- package/dist/elements/index.d.cts +0 -658
- package/dist/elements/index.d.ts +0 -658
- package/dist/elements/index.js +0 -913
- package/dist/plugins/index.cjs +0 -1174
- package/dist/plugins/index.d.ts +0 -731
- package/dist/plugins/index.js +0 -1140
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Auto-generated create-editor script
|
|
4
|
+
# Generated from klint-editor working folder
|
|
5
|
+
|
|
6
|
+
PROJECT_DIR="${1:-.}"
|
|
7
|
+
PROJECT_NAME=$(basename "$PROJECT_DIR")
|
|
8
|
+
|
|
9
|
+
echo "Creating Klint editor in $PROJECT_DIR..."
|
|
10
|
+
|
|
11
|
+
# Create directory if it doesn't exist
|
|
12
|
+
mkdir -p "$PROJECT_DIR"
|
|
13
|
+
cd "$PROJECT_DIR"
|
|
14
|
+
|
|
15
|
+
# Create src directory
|
|
16
|
+
mkdir -p src
|
|
17
|
+
|
|
18
|
+
# Create package.json
|
|
19
|
+
cat > package.json << EOF
|
|
20
|
+
{
|
|
21
|
+
"name": "$PROJECT_NAME",
|
|
22
|
+
"version": "0.1.0",
|
|
23
|
+
"private": true,
|
|
24
|
+
"type": "module",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "vite",
|
|
27
|
+
"build": "vite build",
|
|
28
|
+
"preview": "vite preview"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@monaco-editor/react": "^4.7.0",
|
|
32
|
+
"@shopify/klint": "^0.0.98",
|
|
33
|
+
"react": "^18.3.1",
|
|
34
|
+
"react-dom": "^18.3.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/react": "^18.3.3",
|
|
38
|
+
"@types/react-dom": "^18.3.0",
|
|
39
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
40
|
+
"typescript": "^5.5.4",
|
|
41
|
+
"vite": "^5.4.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
EOF
|
|
45
|
+
|
|
46
|
+
# Create vite.config.ts
|
|
47
|
+
cat > vite.config.ts << 'EOF'
|
|
48
|
+
import { defineConfig } from 'vite';
|
|
49
|
+
import react from '@vitejs/plugin-react';
|
|
50
|
+
|
|
51
|
+
export default defineConfig({
|
|
52
|
+
plugins: [react()],
|
|
53
|
+
});
|
|
54
|
+
EOF
|
|
55
|
+
|
|
56
|
+
# Create tsconfig.json
|
|
57
|
+
cat > tsconfig.json << 'EOF'
|
|
58
|
+
{
|
|
59
|
+
"compilerOptions": {
|
|
60
|
+
"target": "ES2020",
|
|
61
|
+
"useDefineForClassFields": true,
|
|
62
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
63
|
+
"module": "ESNext",
|
|
64
|
+
"skipLibCheck": true,
|
|
65
|
+
|
|
66
|
+
/* Bundler mode */
|
|
67
|
+
"moduleResolution": "bundler",
|
|
68
|
+
"allowImportingTsExtensions": true,
|
|
69
|
+
"resolveJsonModule": true,
|
|
70
|
+
"isolatedModules": true,
|
|
71
|
+
"noEmit": true,
|
|
72
|
+
"jsx": "react-jsx",
|
|
73
|
+
|
|
74
|
+
/* Linting */
|
|
75
|
+
"strict": true,
|
|
76
|
+
"noUnusedLocals": true,
|
|
77
|
+
"noUnusedParameters": true,
|
|
78
|
+
"noFallthroughCasesInSwitch": true
|
|
79
|
+
},
|
|
80
|
+
"include": ["src"],
|
|
81
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
82
|
+
}
|
|
83
|
+
EOF
|
|
84
|
+
|
|
85
|
+
# Create tsconfig.node.json
|
|
86
|
+
cat > tsconfig.node.json << 'EOF'
|
|
87
|
+
{
|
|
88
|
+
"compilerOptions": {
|
|
89
|
+
"composite": true,
|
|
90
|
+
"skipLibCheck": true,
|
|
91
|
+
"module": "ESNext",
|
|
92
|
+
"moduleResolution": "bundler",
|
|
93
|
+
"allowSyntheticDefaultImports": true
|
|
94
|
+
},
|
|
95
|
+
"include": ["vite.config.ts"]
|
|
96
|
+
}
|
|
97
|
+
EOF
|
|
98
|
+
|
|
99
|
+
# Create index.html
|
|
100
|
+
cat > index.html << 'EOF'
|
|
101
|
+
<!doctype html>
|
|
102
|
+
<html lang="en">
|
|
103
|
+
<head>
|
|
104
|
+
<meta charset="UTF-8" />
|
|
105
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
106
|
+
<title>Klint Editor</title>
|
|
107
|
+
</head>
|
|
108
|
+
<body>
|
|
109
|
+
<div id="root"></div>
|
|
110
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
111
|
+
</body>
|
|
112
|
+
</html>
|
|
113
|
+
EOF
|
|
114
|
+
|
|
115
|
+
# Create src/main.tsx
|
|
116
|
+
cat > src/main.tsx << 'EOF'
|
|
117
|
+
import React from "react";
|
|
118
|
+
import ReactDOM from "react-dom/client";
|
|
119
|
+
import App from "./App.tsx";
|
|
120
|
+
|
|
121
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
122
|
+
<React.StrictMode>
|
|
123
|
+
<App />
|
|
124
|
+
</React.StrictMode>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
EOF
|
|
128
|
+
|
|
129
|
+
# Create src/App.tsx
|
|
130
|
+
cat > src/App.tsx << 'EOF'
|
|
131
|
+
import { useState, useEffect, useRef, useCallback } from "react";
|
|
132
|
+
import { Editor } from "@monaco-editor/react";
|
|
133
|
+
import { useKlint, Klint, type KlintContext } from "@shopify/klint";
|
|
134
|
+
|
|
135
|
+
const defaultCode = `
|
|
136
|
+
function preload(K) {
|
|
137
|
+
console.log(K, "Welcome to Klint Editor! 🎨✨");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function setup(K) {
|
|
141
|
+
K.textFont("Inter");
|
|
142
|
+
K.textSize(64);
|
|
143
|
+
K.noStroke();
|
|
144
|
+
K.alignText("center", "middle");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function draw(K) {
|
|
148
|
+
K.background('rgba(125, 0, 255, 255)');
|
|
149
|
+
K.fillColor("#FFF");
|
|
150
|
+
// Mouse is available directly on K after being extended in preload
|
|
151
|
+
const mouseX = K.mouse ? K.mouse.x : K.width * 0.5;
|
|
152
|
+
const mouseY = K.mouse ? K.mouse.y : K.height * 0.5;
|
|
153
|
+
K.circle(mouseX, mouseY + Math.sin(K.frame * 0.03) * 100, 50);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
// Define the return type for evaluated user code
|
|
159
|
+
interface UserCode {
|
|
160
|
+
preload?: (K: KlintContext) => Promise<void> | void;
|
|
161
|
+
setup?: (K: KlintContext) => void;
|
|
162
|
+
draw?: (K: KlintContext) => void;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Separate Klint Canvas component
|
|
166
|
+
function KlintCanvas({ code }: { code: string }) {
|
|
167
|
+
const klintHook = useKlint();
|
|
168
|
+
const { KlintMouse } = klintHook;
|
|
169
|
+
const mouseHook = KlintMouse();
|
|
170
|
+
const [error, setError] = useState<string | null>(null);
|
|
171
|
+
const [isClient, setIsClient] = useState(false);
|
|
172
|
+
|
|
173
|
+
// Set isClient to true after component mounts
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
setIsClient(true);
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
// Create a sandbox environment with hooks available
|
|
179
|
+
const createSandbox = useCallback((): UserCode => {
|
|
180
|
+
try {
|
|
181
|
+
// Create a mock context for code evaluation (not runtime execution)
|
|
182
|
+
// The real context will be passed to preload/setup/draw when they're called
|
|
183
|
+
const sandbox = {
|
|
184
|
+
K: {
|
|
185
|
+
// Provide mock mouse hook access for code evaluation
|
|
186
|
+
useMouse: () => mouseHook,
|
|
187
|
+
// Add other hooks here as needed
|
|
188
|
+
},
|
|
189
|
+
console: console,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Evaluate the code in the sandbox
|
|
193
|
+
const userCode = new Function(
|
|
194
|
+
"K",
|
|
195
|
+
`
|
|
196
|
+
"use strict";
|
|
197
|
+
let userPreload, userSetup, userDraw;
|
|
198
|
+
|
|
199
|
+
// Make hooks available in global scope
|
|
200
|
+
const useMouse = K.useMouse;
|
|
201
|
+
|
|
202
|
+
// Execute user code
|
|
203
|
+
${code}
|
|
204
|
+
|
|
205
|
+
// Return the lifecycle functions
|
|
206
|
+
return {
|
|
207
|
+
preload: preload || userPreload,
|
|
208
|
+
setup: setup || userSetup,
|
|
209
|
+
draw: draw || userDraw
|
|
210
|
+
};
|
|
211
|
+
`
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// Execute the code with the sandbox
|
|
215
|
+
const result = userCode(sandbox.K) as UserCode;
|
|
216
|
+
setError(null);
|
|
217
|
+
return result;
|
|
218
|
+
} catch (err) {
|
|
219
|
+
console.error("Error evaluating code:", err);
|
|
220
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
221
|
+
return { preload: undefined, setup: undefined, draw: undefined };
|
|
222
|
+
}
|
|
223
|
+
}, [code, mouseHook]);
|
|
224
|
+
|
|
225
|
+
// Create a ref to hold the evaluated code
|
|
226
|
+
const userCodeRef = useRef<UserCode | null>(null);
|
|
227
|
+
|
|
228
|
+
// Update the code when it changes
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (isClient) {
|
|
231
|
+
userCodeRef.current = createSandbox();
|
|
232
|
+
}
|
|
233
|
+
}, [isClient, createSandbox]);
|
|
234
|
+
|
|
235
|
+
const preload = useCallback(
|
|
236
|
+
async (K: KlintContext) => {
|
|
237
|
+
// Extend K with mouse data directly
|
|
238
|
+
K.extend("mouse", mouseHook.mouse);
|
|
239
|
+
// Add mouse event handlers
|
|
240
|
+
K.extend("onMouseClick", mouseHook.onClick);
|
|
241
|
+
K.extend("onMouseIn", mouseHook.onMouseIn);
|
|
242
|
+
K.extend("onMouseOut", mouseHook.onMouseOut);
|
|
243
|
+
K.extend("onMouseDown", mouseHook.onMouseDown);
|
|
244
|
+
K.extend("onMouseUp", mouseHook.onMouseUp);
|
|
245
|
+
|
|
246
|
+
if (userCodeRef.current?.preload) {
|
|
247
|
+
try {
|
|
248
|
+
await userCodeRef.current.preload(K);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
console.error("Error in preload:", err);
|
|
251
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
[mouseHook]
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const setup = useCallback((K: KlintContext) => {
|
|
259
|
+
if (userCodeRef.current?.setup) {
|
|
260
|
+
try {
|
|
261
|
+
userCodeRef.current.setup(K);
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error("Error in setup:", err);
|
|
264
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}, []);
|
|
268
|
+
|
|
269
|
+
const draw = useCallback((K: KlintContext) => {
|
|
270
|
+
if (userCodeRef.current?.draw) {
|
|
271
|
+
try {
|
|
272
|
+
userCodeRef.current.draw(K);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
console.error("Error in draw:", err);
|
|
275
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}, []);
|
|
279
|
+
|
|
280
|
+
if (!isClient) {
|
|
281
|
+
return (
|
|
282
|
+
<div style={{ width: "100%", height: "100%", background: "#000" }}></div>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (error) {
|
|
287
|
+
return (
|
|
288
|
+
<div
|
|
289
|
+
style={{
|
|
290
|
+
width: "100%",
|
|
291
|
+
height: "100%",
|
|
292
|
+
background: "#300",
|
|
293
|
+
color: "#f88",
|
|
294
|
+
padding: "20px",
|
|
295
|
+
fontFamily: "monospace",
|
|
296
|
+
whiteSpace: "pre-wrap",
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
299
|
+
{error}
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<Klint
|
|
306
|
+
context={klintHook.context}
|
|
307
|
+
preload={preload}
|
|
308
|
+
setup={setup}
|
|
309
|
+
draw={draw}
|
|
310
|
+
options={{
|
|
311
|
+
origin: "corner",
|
|
312
|
+
unsafemode: "true",
|
|
313
|
+
}}
|
|
314
|
+
/>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function App() {
|
|
319
|
+
const [code, setCode] = useState(defaultCode);
|
|
320
|
+
const [runningCode, setRunningCode] = useState(defaultCode);
|
|
321
|
+
const [isClient, setIsClient] = useState(false);
|
|
322
|
+
const [canvasKey, setCanvasKey] = useState(0); // Add a key to force remount
|
|
323
|
+
|
|
324
|
+
// Set isClient to true after component mounts
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
setIsClient(true);
|
|
327
|
+
}, []);
|
|
328
|
+
|
|
329
|
+
// Function to run code and clear console
|
|
330
|
+
const runCode = useCallback(() => {
|
|
331
|
+
// Clear the console before running new code
|
|
332
|
+
console.clear();
|
|
333
|
+
setRunningCode(code);
|
|
334
|
+
// Increment the key to force a complete remount of the KlintCanvas
|
|
335
|
+
setCanvasKey((prev) => prev + 1);
|
|
336
|
+
}, [code]);
|
|
337
|
+
|
|
338
|
+
// Function to clear the editor
|
|
339
|
+
const clearCode = useCallback(() => {
|
|
340
|
+
setCode("");
|
|
341
|
+
setRunningCode("");
|
|
342
|
+
setCanvasKey((prev) => prev + 1);
|
|
343
|
+
}, []);
|
|
344
|
+
|
|
345
|
+
if (!isClient) {
|
|
346
|
+
return <div style={{ height: "100vh", background: "#1e1e1e" }}></div>;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
351
|
+
<div style={{ display: "flex", gap: "8px", margin: "8px" }}>
|
|
352
|
+
<button
|
|
353
|
+
onClick={runCode}
|
|
354
|
+
style={{
|
|
355
|
+
padding: "8px 16px",
|
|
356
|
+
backgroundColor: "#4CAF50",
|
|
357
|
+
color: "white",
|
|
358
|
+
border: "none",
|
|
359
|
+
borderRadius: "4px",
|
|
360
|
+
cursor: "pointer",
|
|
361
|
+
}}
|
|
362
|
+
>
|
|
363
|
+
Run
|
|
364
|
+
</button>
|
|
365
|
+
<button
|
|
366
|
+
onClick={clearCode}
|
|
367
|
+
style={{
|
|
368
|
+
padding: "8px 16px",
|
|
369
|
+
backgroundColor: "#f44336",
|
|
370
|
+
color: "white",
|
|
371
|
+
border: "none",
|
|
372
|
+
borderRadius: "4px",
|
|
373
|
+
cursor: "pointer",
|
|
374
|
+
}}
|
|
375
|
+
>
|
|
376
|
+
Clear
|
|
377
|
+
</button>
|
|
378
|
+
</div>
|
|
379
|
+
<div style={{ display: "flex", flex: 1 }}>
|
|
380
|
+
<div style={{ flex: 1 }}>
|
|
381
|
+
<Editor
|
|
382
|
+
height="100%"
|
|
383
|
+
defaultLanguage="javascript"
|
|
384
|
+
value={code}
|
|
385
|
+
onChange={(value) => setCode(value || "")}
|
|
386
|
+
options={{
|
|
387
|
+
minimap: { enabled: false },
|
|
388
|
+
fontSize: 14,
|
|
389
|
+
theme: "vs-dark",
|
|
390
|
+
}}
|
|
391
|
+
onMount={(editor) => {
|
|
392
|
+
editor.updateOptions({ theme: "vs-dark" });
|
|
393
|
+
}}
|
|
394
|
+
/>
|
|
395
|
+
</div>
|
|
396
|
+
<div style={{ flex: 1, background: "#000" }}>
|
|
397
|
+
<KlintCanvas key={`${canvasKey}-${runningCode}`} code={runningCode} />
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export default App;
|
|
405
|
+
|
|
406
|
+
EOF
|
|
407
|
+
|
|
408
|
+
# Create .gitignore
|
|
409
|
+
cat > .gitignore << 'EOF'
|
|
410
|
+
node_modules
|
|
411
|
+
dist
|
|
412
|
+
.env
|
|
413
|
+
EOF
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
echo "✅ Klint editor created successfully!"
|
|
417
|
+
echo ""
|
|
418
|
+
echo "Next steps:"
|
|
419
|
+
echo " cd $PROJECT_DIR"
|
|
420
|
+
echo " npm install"
|
|
421
|
+
echo " npm run dev"
|
|
422
|
+
echo ""
|
|
423
|
+
echo "The editor will be available at http://localhost:5173"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Klint Dev Sandbox Generator
|
|
4
|
+
# Creates a lightweight Vite setup for quick Klint prototyping
|
|
5
|
+
|
|
6
|
+
PROJECT_DIR="${1:-.}"
|
|
7
|
+
PROJECT_NAME=$(basename "$PROJECT_DIR")
|
|
8
|
+
|
|
9
|
+
echo "🏖️ Creating Klint dev sandbox in $PROJECT_DIR..."
|
|
10
|
+
|
|
11
|
+
# Create directory if it doesn't exist
|
|
12
|
+
mkdir -p "$PROJECT_DIR"
|
|
13
|
+
cd "$PROJECT_DIR"
|
|
14
|
+
|
|
15
|
+
# Create src directory
|
|
16
|
+
mkdir -p src
|
|
17
|
+
|
|
18
|
+
# Create package.json
|
|
19
|
+
cat > package.json << EOF
|
|
20
|
+
{
|
|
21
|
+
"name": "$PROJECT_NAME",
|
|
22
|
+
"version": "0.1.0",
|
|
23
|
+
"private": true,
|
|
24
|
+
"type": "module",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "vite",
|
|
27
|
+
"build": "vite build",
|
|
28
|
+
"preview": "vite preview"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@shopify/klint": "^0.0.98",
|
|
32
|
+
"react": "^18.3.1",
|
|
33
|
+
"react-dom": "^18.3.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^18.3.3",
|
|
37
|
+
"@types/react-dom": "^18.3.0",
|
|
38
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
39
|
+
"typescript": "^5.5.4",
|
|
40
|
+
"vite": "^5.4.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
EOF
|
|
44
|
+
|
|
45
|
+
# Create vite.config.ts
|
|
46
|
+
cat > vite.config.ts << 'EOF'
|
|
47
|
+
import { defineConfig } from 'vite';
|
|
48
|
+
import react from '@vitejs/plugin-react';
|
|
49
|
+
|
|
50
|
+
export default defineConfig({
|
|
51
|
+
plugins: [react()],
|
|
52
|
+
server: {
|
|
53
|
+
port: 3000,
|
|
54
|
+
open: true
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
EOF
|
|
58
|
+
|
|
59
|
+
# Create tsconfig.json
|
|
60
|
+
cat > tsconfig.json << 'EOF'
|
|
61
|
+
{
|
|
62
|
+
"compilerOptions": {
|
|
63
|
+
"target": "ES2020",
|
|
64
|
+
"useDefineForClassFields": true,
|
|
65
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
66
|
+
"module": "ESNext",
|
|
67
|
+
"skipLibCheck": true,
|
|
68
|
+
"moduleResolution": "bundler",
|
|
69
|
+
"allowImportingTsExtensions": true,
|
|
70
|
+
"resolveJsonModule": true,
|
|
71
|
+
"isolatedModules": true,
|
|
72
|
+
"noEmit": true,
|
|
73
|
+
"jsx": "react-jsx",
|
|
74
|
+
"strict": true,
|
|
75
|
+
"noUnusedLocals": false,
|
|
76
|
+
"noUnusedParameters": false,
|
|
77
|
+
"noFallthroughCasesInSwitch": true
|
|
78
|
+
},
|
|
79
|
+
"include": ["src"],
|
|
80
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
81
|
+
}
|
|
82
|
+
EOF
|
|
83
|
+
|
|
84
|
+
# Create tsconfig.node.json
|
|
85
|
+
cat > tsconfig.node.json << 'EOF'
|
|
86
|
+
{
|
|
87
|
+
"compilerOptions": {
|
|
88
|
+
"composite": true,
|
|
89
|
+
"skipLibCheck": true,
|
|
90
|
+
"module": "ESNext",
|
|
91
|
+
"moduleResolution": "bundler",
|
|
92
|
+
"allowSyntheticDefaultImports": true
|
|
93
|
+
},
|
|
94
|
+
"include": ["vite.config.ts"]
|
|
95
|
+
}
|
|
96
|
+
EOF
|
|
97
|
+
|
|
98
|
+
# Create index.html
|
|
99
|
+
cat > index.html << 'EOF'
|
|
100
|
+
<!doctype html>
|
|
101
|
+
<html lang="en">
|
|
102
|
+
<head>
|
|
103
|
+
<meta charset="UTF-8" />
|
|
104
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
105
|
+
<title>Klint Sandbox</title>
|
|
106
|
+
<style>
|
|
107
|
+
body { margin: 0; padding: 0; background: #111; }
|
|
108
|
+
#root { width: 100vw; height: 100vh; }
|
|
109
|
+
</style>
|
|
110
|
+
</head>
|
|
111
|
+
<body>
|
|
112
|
+
<div id="root"></div>
|
|
113
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
114
|
+
</body>
|
|
115
|
+
</html>
|
|
116
|
+
EOF
|
|
117
|
+
|
|
118
|
+
# Create src/main.tsx
|
|
119
|
+
cat > src/main.tsx << 'EOF'
|
|
120
|
+
import React from "react";
|
|
121
|
+
import ReactDOM from "react-dom/client";
|
|
122
|
+
import App from "./App.tsx";
|
|
123
|
+
|
|
124
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
125
|
+
<React.StrictMode>
|
|
126
|
+
<App />
|
|
127
|
+
</React.StrictMode>
|
|
128
|
+
);
|
|
129
|
+
EOF
|
|
130
|
+
|
|
131
|
+
# Create src/App.tsx with a simple example
|
|
132
|
+
cat > src/App.tsx << 'EOF'
|
|
133
|
+
import { useKlint, Klint } from "@shopify/klint";
|
|
134
|
+
|
|
135
|
+
export default function App() {
|
|
136
|
+
const { context, KlintMouse } = useKlint();
|
|
137
|
+
const mouse = KlintMouse();
|
|
138
|
+
|
|
139
|
+
const setup = (K: any) => {
|
|
140
|
+
K.textFont("Inter, sans-serif");
|
|
141
|
+
K.textSize(48);
|
|
142
|
+
K.alignText("center", "middle");
|
|
143
|
+
K.noStroke();
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const draw = (K: any) => {
|
|
147
|
+
// Background with trailing effect
|
|
148
|
+
K.background("rgba(20, 20, 40, 0.1)");
|
|
149
|
+
|
|
150
|
+
// Mouse-following circle
|
|
151
|
+
const mouseX = mouse.x || K.width / 2;
|
|
152
|
+
const mouseY = mouse.y || K.height / 2;
|
|
153
|
+
|
|
154
|
+
// Animated circle
|
|
155
|
+
K.fillColor("#00ff88");
|
|
156
|
+
const size = 50 + Math.sin(K.frame * 0.02) * 20;
|
|
157
|
+
K.circle(mouseX, mouseY, size);
|
|
158
|
+
|
|
159
|
+
// Some text
|
|
160
|
+
K.fillColor("#ffffff");
|
|
161
|
+
K.text("Klint Dev Sandbox", K.width / 2, K.height / 2 + 150);
|
|
162
|
+
K.textSize(24);
|
|
163
|
+
K.text("Move your mouse around!", K.width / 2, K.height / 2 + 200);
|
|
164
|
+
|
|
165
|
+
// Corner info
|
|
166
|
+
K.textSize(16);
|
|
167
|
+
K.alignText("left", "top");
|
|
168
|
+
K.text(\`Frame: \${K.frame}\`, 20, 20);
|
|
169
|
+
K.text(\`Mouse: \${Math.round(mouseX)}, \${Math.round(mouseY)}\`, 20, 40);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<Klint
|
|
174
|
+
context={context}
|
|
175
|
+
setup={setup}
|
|
176
|
+
draw={draw}
|
|
177
|
+
options={{
|
|
178
|
+
origin: "corner"
|
|
179
|
+
}}
|
|
180
|
+
/>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
EOF
|
|
184
|
+
|
|
185
|
+
# Create .gitignore
|
|
186
|
+
cat > .gitignore << 'EOF'
|
|
187
|
+
node_modules
|
|
188
|
+
dist
|
|
189
|
+
.env
|
|
190
|
+
.DS_Store
|
|
191
|
+
EOF
|
|
192
|
+
|
|
193
|
+
# Create README.md
|
|
194
|
+
cat > README.md << 'EOF'
|
|
195
|
+
# Klint Dev Sandbox
|
|
196
|
+
|
|
197
|
+
A lightweight development environment for prototyping with Klint.
|
|
198
|
+
|
|
199
|
+
## Quick Start
|
|
200
|
+
|
|
201
|
+
\`\`\`bash
|
|
202
|
+
npm install
|
|
203
|
+
npm run dev
|
|
204
|
+
\`\`\`
|
|
205
|
+
|
|
206
|
+
## What's included
|
|
207
|
+
|
|
208
|
+
- ⚡ Vite for fast hot reload
|
|
209
|
+
- 🎨 Klint pre-configured
|
|
210
|
+
- 🖱️ Mouse interactions ready
|
|
211
|
+
- 📱 Responsive canvas
|
|
212
|
+
|
|
213
|
+
## Tips
|
|
214
|
+
|
|
215
|
+
- Edit \`src/App.tsx\` to create your sketch
|
|
216
|
+
- Use \`setup(K)\` for initialization
|
|
217
|
+
- Use \`draw(K)\` for your main loop
|
|
218
|
+
- The canvas fills the entire viewport
|
|
219
|
+
- Hot reload updates instantly
|
|
220
|
+
|
|
221
|
+
Happy creative coding! 🎨
|
|
222
|
+
EOF
|
|
223
|
+
|
|
224
|
+
echo "✅ Klint dev sandbox created successfully!"
|
|
225
|
+
echo ""
|
|
226
|
+
echo "🚀 Quick start:"
|
|
227
|
+
echo " cd $PROJECT_DIR"
|
|
228
|
+
echo " npm install"
|
|
229
|
+
echo " npm run dev"
|
|
230
|
+
echo ""
|
|
231
|
+
echo "The sandbox will open at http://localhost:3000"
|
|
232
|
+
echo "Perfect for quick prototyping and creative experiments! 🎨"
|