@1agh/maude 0.17.2 → 0.18.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/cli/commands/design.mjs +92 -29
- package/package.json +8 -8
- package/plugins/design/dev-server/bin/screenshot.sh +12 -0
- package/plugins/design/dev-server/boot-self-heal.ts +87 -0
- package/plugins/design/dev-server/build.ts +56 -2
- package/plugins/design/dev-server/config.schema.json +12 -0
- package/plugins/design/dev-server/dist/client.bundle.js +3 -3
- package/plugins/design/dev-server/dist/runtime/pixi-js.js +52708 -0
- package/plugins/design/dev-server/dist/runtime/react-dom.js +249 -0
- package/plugins/design/dev-server/dist/runtime/react-dom_client.js +10375 -0
- package/plugins/design/dev-server/dist/runtime/react.js +553 -0
- package/plugins/design/dev-server/dist/runtime/react_jsx-dev-runtime.js +78 -0
- package/plugins/design/dev-server/dist/runtime/react_jsx-runtime.js +103 -0
- package/plugins/design/dev-server/http.ts +5 -3
- package/plugins/design/dev-server/paths.ts +115 -0
- package/plugins/design/dev-server/runtime-bundle.ts +80 -7
- package/plugins/design/dev-server/server.ts +6 -0
- package/plugins/design/dev-server/test/boot-self-heal.test.ts +87 -0
- package/plugins/design/dev-server/test/paths.test.ts +31 -0
- package/plugins/design/dev-server/test/runtime-bundle-error-mapping.test.ts +43 -0
- package/plugins/design/templates/canvas.tsx.template +7 -7
- package/plugins/design/templates/design-system-inspiration/audience-pro/colors-presence.html +1 -1
- package/plugins/design/templates/design-system-inspiration/core/README.philosophy.md.tpl +11 -7
- package/plugins/design/templates/design-system-inspiration/core/SKILL.md.tpl +4 -3
- package/plugins/design/templates/design-system-inspiration/core/colors_and_type.css.tpl +61 -57
- package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +2 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/colors-accent.html +4 -4
- package/plugins/design/templates/design-system-inspiration/meta/presence-multiplayer.html +1 -1
- package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-showcase.html +3 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-showcase.html +1 -1
- package/plugins/design/templates/design-system-inspiration/theme-both/colors-themes-side-by-side.html +6 -6
- package/plugins/design/templates/design-system-inspiration/universal/components-dialogs.html +3 -2
- package/plugins/design/templates/design-system-inspiration/universal/logo.html +2 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
function __accessProp(key) {
|
|
7
|
+
return this[key];
|
|
8
|
+
}
|
|
9
|
+
var __toESMCache_node;
|
|
10
|
+
var __toESMCache_esm;
|
|
11
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
12
|
+
var canCache = mod != null && typeof mod === "object";
|
|
13
|
+
if (canCache) {
|
|
14
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
15
|
+
var cached = cache.get(mod);
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
20
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
21
|
+
for (let key of __getOwnPropNames(mod))
|
|
22
|
+
if (!__hasOwnProp.call(to, key))
|
|
23
|
+
__defProp(to, key, {
|
|
24
|
+
get: __accessProp.bind(mod, key),
|
|
25
|
+
enumerable: true
|
|
26
|
+
});
|
|
27
|
+
if (canCache)
|
|
28
|
+
cache.set(mod, to);
|
|
29
|
+
return to;
|
|
30
|
+
};
|
|
31
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
32
|
+
var __returnValue = (v) => v;
|
|
33
|
+
function __exportSetter(name, newValue) {
|
|
34
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
35
|
+
}
|
|
36
|
+
var __export = (target, all) => {
|
|
37
|
+
for (var name in all)
|
|
38
|
+
__defProp(target, name, {
|
|
39
|
+
get: all[name],
|
|
40
|
+
enumerable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
set: __exportSetter.bind(all, name)
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
46
|
+
|
|
47
|
+
// ../../../node_modules/.pnpm/react@19.2.6/node_modules/react/cjs/react-jsx-runtime.production.js
|
|
48
|
+
var exports_react_jsx_runtime_production = {};
|
|
49
|
+
__export(exports_react_jsx_runtime_production, {
|
|
50
|
+
jsxs: () => $jsxs,
|
|
51
|
+
jsx: () => $jsx,
|
|
52
|
+
Fragment: () => $Fragment
|
|
53
|
+
});
|
|
54
|
+
function jsxProd(type, config, maybeKey) {
|
|
55
|
+
var key = null;
|
|
56
|
+
maybeKey !== undefined && (key = "" + maybeKey);
|
|
57
|
+
config.key !== undefined && (key = "" + config.key);
|
|
58
|
+
if ("key" in config) {
|
|
59
|
+
maybeKey = {};
|
|
60
|
+
for (var propName in config)
|
|
61
|
+
propName !== "key" && (maybeKey[propName] = config[propName]);
|
|
62
|
+
} else
|
|
63
|
+
maybeKey = config;
|
|
64
|
+
config = maybeKey.ref;
|
|
65
|
+
return {
|
|
66
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
67
|
+
type,
|
|
68
|
+
key,
|
|
69
|
+
ref: config !== undefined ? config : null,
|
|
70
|
+
props: maybeKey
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
var REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, $Fragment, $jsx, $jsxs;
|
|
74
|
+
var init_react_jsx_runtime_production = __esm(() => {
|
|
75
|
+
REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
|
|
76
|
+
REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
|
|
77
|
+
$Fragment = REACT_FRAGMENT_TYPE;
|
|
78
|
+
$jsx = jsxProd;
|
|
79
|
+
$jsxs = jsxProd;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ../../../node_modules/.pnpm/react@19.2.6/node_modules/react/jsx-runtime.js
|
|
83
|
+
var require_jsx_runtime = __commonJS((exports, module) => {
|
|
84
|
+
init_react_jsx_runtime_production();
|
|
85
|
+
if (true) {
|
|
86
|
+
module.exports = exports_react_jsx_runtime_production;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// synth:/home/runner/work/maude/maude/plugins/design/dev-server/.runtime-bundle-react_jsx-runtime-entry.tsx
|
|
91
|
+
var __mod__ = __toESM(require_jsx_runtime(), 1);
|
|
92
|
+
var {
|
|
93
|
+
Fragment,
|
|
94
|
+
jsx,
|
|
95
|
+
jsxs
|
|
96
|
+
} = __mod__;
|
|
97
|
+
var __runtime_bundle_react_jsx_runtime_entry_default = __mod__;
|
|
98
|
+
export {
|
|
99
|
+
jsxs,
|
|
100
|
+
jsx,
|
|
101
|
+
__runtime_bundle_react_jsx_runtime_entry_default as default,
|
|
102
|
+
Fragment
|
|
103
|
+
};
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
// top-level fall-through for paths Bun's `routes` field doesn't cover.
|
|
6
6
|
|
|
7
7
|
import { watch } from 'node:fs';
|
|
8
|
-
import {
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { join, posix } from 'node:path';
|
|
10
9
|
|
|
11
10
|
import type { Api } from './api.ts';
|
|
12
11
|
import { buildCanvasModule } from './canvas-build.ts';
|
|
@@ -17,9 +16,12 @@ import { isFormat, isScope, runExport } from './exporters/index.ts';
|
|
|
17
16
|
import type { ActiveJsonShape } from './exporters/scope.ts';
|
|
18
17
|
import type { Inspect } from './inspect.ts';
|
|
19
18
|
import { canvasSlug, writeLocator } from './locator.ts';
|
|
19
|
+
import { DEV_SERVER_ROOT } from './paths.ts';
|
|
20
20
|
import { RUNTIME_PACKAGES, getRuntimeBundle, packageForSlug, slugFor } from './runtime-bundle.ts';
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// Real disk install root — never the virtual `/$bunfs/root` of compiled bins.
|
|
23
|
+
// See paths.ts for the resolution logic + Phase 19.1 / v0.18.1 rationale.
|
|
24
|
+
const HERE = DEV_SERVER_ROOT;
|
|
23
25
|
|
|
24
26
|
export const MIME: Record<string, string> = {
|
|
25
27
|
'.html': 'text/html; charset=utf-8',
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Path resolution for the dev-server, robust across THREE runtime modes:
|
|
2
|
+
//
|
|
3
|
+
// 1. Dev (`bun server.ts`): import.meta.url is a real file:// path,
|
|
4
|
+
// use dirname of it.
|
|
5
|
+
// 2. Compiled binary, npm: maude installed via `npm i -g @1agh/maude`;
|
|
6
|
+
// binary lives in a sub-package dir, deps in
|
|
7
|
+
// the parent @1agh/maude/ dir. import.meta.url
|
|
8
|
+
// is the virtual `/$bunfs/root` (bun --compile
|
|
9
|
+
// embedded fs) — NOT a real disk path.
|
|
10
|
+
// Walk up from process.execPath until we find
|
|
11
|
+
// the real plugins/design/dev-server/ dir.
|
|
12
|
+
// 3. Compiled binary, marketplace cache: similar to (2) but lives at
|
|
13
|
+
// ~/.claude/plugins/cache/maude/design/<v>/
|
|
14
|
+
// dev-server/. Same walk-up logic finds it.
|
|
15
|
+
//
|
|
16
|
+
// Why this matters: Phase 19 v0.18.0 used `dirname(fileURLToPath(import.meta.url))`
|
|
17
|
+
// universally. In the compiled binary that's `/$bunfs/root` — a virtual path —
|
|
18
|
+
// so `existsSync('/$bunfs/root/dist/client.bundle.js')` always returned false
|
|
19
|
+
// even when the file was sitting on disk at the real install path. Self-heal
|
|
20
|
+
// false-triggered, http.ts /_client/* fell through to /$bunfs/root/client/*.jsx
|
|
21
|
+
// (raw source), runtime-bundle.ts synthetic entrypoint anchored in virtual fs
|
|
22
|
+
// so Bun.build couldn't walk node_modules. Every observed symptom traces to
|
|
23
|
+
// this one bug. Phase 19.1 / v0.18.1.
|
|
24
|
+
|
|
25
|
+
import { existsSync } from 'node:fs';
|
|
26
|
+
import { dirname, join } from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Real disk path to the dev-server install dir
|
|
31
|
+
* (`plugins/design/dev-server/` inside whatever package layout we're in).
|
|
32
|
+
*
|
|
33
|
+
* Always a directory that contains `http.ts` + `dist/` + (optionally)
|
|
34
|
+
* `node_modules/` and `client/`. Never a virtual `/$bunfs/*` path.
|
|
35
|
+
*/
|
|
36
|
+
export const DEV_SERVER_ROOT: string = resolveDevServerRoot();
|
|
37
|
+
|
|
38
|
+
/** `<DEV_SERVER_ROOT>/dist/` — committed artifacts + runtime bundles + binary. */
|
|
39
|
+
export const DIST_DIR: string = join(DEV_SERVER_ROOT, 'dist');
|
|
40
|
+
|
|
41
|
+
/** `<DEV_SERVER_ROOT>/client/` — raw source HTML + JSX + CSS for dev fallback. */
|
|
42
|
+
export const CLIENT_DIR: string = join(DEV_SERVER_ROOT, 'client');
|
|
43
|
+
|
|
44
|
+
/** `<DEV_SERVER_ROOT>/dist/runtime/` — pre-built /_canvas-runtime/*.js bundles. */
|
|
45
|
+
export const RUNTIME_BUNDLES_DIR: string = join(DIST_DIR, 'runtime');
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether we are running inside a `bun --compile` standalone binary
|
|
49
|
+
* (true when `import.meta.url` resolves to bun's virtual filesystem).
|
|
50
|
+
*
|
|
51
|
+
* Useful for code that needs to know whether disk-relative fallback paths
|
|
52
|
+
* (e.g. `<DEV_SERVER_ROOT>/client/app.jsx`) are even reachable — in compiled
|
|
53
|
+
* mode the answer is "only if shipped via the install layout".
|
|
54
|
+
*/
|
|
55
|
+
export const IS_COMPILED_BINARY: boolean = isVirtualBunfsPath(getImportMetaDir());
|
|
56
|
+
|
|
57
|
+
function getImportMetaDir(): string | null {
|
|
58
|
+
try {
|
|
59
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isVirtualBunfsPath(p: string | null): boolean {
|
|
66
|
+
return p !== null && (p.startsWith('/$bunfs') || p.startsWith('B:/~BUN'));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isDevServerDir(dir: string): boolean {
|
|
70
|
+
// Two anchors that the dev-server source tree always has and an arbitrary
|
|
71
|
+
// sibling directory wouldn't: http.ts (canonical entry route table) AND
|
|
72
|
+
// package.json with name "@maude/dev-server" (catches the case where some
|
|
73
|
+
// unrelated tree happens to have an http.ts).
|
|
74
|
+
if (!existsSync(join(dir, 'http.ts'))) return false;
|
|
75
|
+
const pkgPath = join(dir, 'package.json');
|
|
76
|
+
if (!existsSync(pkgPath)) return false;
|
|
77
|
+
try {
|
|
78
|
+
const text = require('node:fs').readFileSync(pkgPath, 'utf8') as string;
|
|
79
|
+
return /"@maude\/dev-server"/.test(text);
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveDevServerRoot(): string {
|
|
86
|
+
// (1) Dev mode: import.meta.url is a real file:// path AND lands in the
|
|
87
|
+
// dev-server dir. Common case for `bun run server.ts`, tests, etc.
|
|
88
|
+
const importDir = getImportMetaDir();
|
|
89
|
+
if (importDir && !isVirtualBunfsPath(importDir) && isDevServerDir(importDir)) {
|
|
90
|
+
return importDir;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// (2 + 3) Compiled binary: walk up from process.execPath until we find a dir
|
|
94
|
+
// that *contains* `plugins/design/dev-server/<canonical files>`. Match both
|
|
95
|
+
// npm install layout (binary at @1agh/maude-<plat>/maude → walk up 4 levels
|
|
96
|
+
// to @1agh/maude/) AND marketplace cache layout (binary somewhere under
|
|
97
|
+
// ~/.claude/plugins/cache/maude/design/<v>/dev-server/dist/).
|
|
98
|
+
let cur = dirname(process.execPath);
|
|
99
|
+
for (let i = 0; i < 10; i++) {
|
|
100
|
+
// Check if cur itself is the dev-server root.
|
|
101
|
+
if (isDevServerDir(cur)) return cur;
|
|
102
|
+
// Check if cur contains plugins/design/dev-server/.
|
|
103
|
+
const nested = join(cur, 'plugins', 'design', 'dev-server');
|
|
104
|
+
if (isDevServerDir(nested)) return nested;
|
|
105
|
+
const parent = dirname(cur);
|
|
106
|
+
if (parent === cur) break;
|
|
107
|
+
cur = parent;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Final fallback for unanchored test contexts (e.g. tests spawning compiled
|
|
111
|
+
// binary in a tmp dir without our layout). Return the import dir even if
|
|
112
|
+
// it's virtual — callers should expect existsSync to fail and surface a
|
|
113
|
+
// clear error.
|
|
114
|
+
return importDir ?? dirname(process.execPath);
|
|
115
|
+
}
|
|
@@ -18,10 +18,28 @@
|
|
|
18
18
|
// - In dev we externalise *nothing* — the four bundles together are
|
|
19
19
|
// self-contained. The importmap wires them together at runtime.
|
|
20
20
|
|
|
21
|
-
import {
|
|
22
|
-
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { join } from 'node:path';
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
import { DEV_SERVER_ROOT, RUNTIME_BUNDLES_DIR } from './paths.ts';
|
|
24
|
+
|
|
25
|
+
// Real disk install root — synthetic entry points must anchor here so
|
|
26
|
+
// Bun.build's resolver walks UP and finds node_modules/react on disk.
|
|
27
|
+
// In compiled binaries, import.meta.url is the virtual `/$bunfs/root`
|
|
28
|
+
// where no node_modules exists (Phase 19.1 / v0.18.1).
|
|
29
|
+
const HERE = DEV_SERVER_ROOT;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read a pre-built runtime bundle from `dist/runtime/<slug>.js`. Returns
|
|
33
|
+
* null when missing → caller falls back to dynamic Bun.build (dev mode).
|
|
34
|
+
*/
|
|
35
|
+
async function loadPrebuiltRuntimeBundle(pkg: RuntimePackage): Promise<BundleCacheEntry | null> {
|
|
36
|
+
const path = join(RUNTIME_BUNDLES_DIR, `${slugFor(pkg)}.js`);
|
|
37
|
+
const file = Bun.file(path);
|
|
38
|
+
if (!(await file.exists())) return null;
|
|
39
|
+
const js = await file.text();
|
|
40
|
+
if (!js) return null;
|
|
41
|
+
return { js, etag: Bun.hash(js).toString(16) };
|
|
42
|
+
}
|
|
25
43
|
|
|
26
44
|
export const RUNTIME_PACKAGES = [
|
|
27
45
|
'react',
|
|
@@ -99,10 +117,37 @@ const cache = new Map<RuntimePackage, BundleCacheEntry>();
|
|
|
99
117
|
* each entry includes everything it needs; the four bundles only share state
|
|
100
118
|
* at the browser level (via React's module-singleton convention — multiple
|
|
101
119
|
* imports of "react" resolve to the same module thanks to the importmap).
|
|
120
|
+
*
|
|
121
|
+
* Two paths:
|
|
122
|
+
* 1. **Pre-built on disk** (Phase 19.1 / v0.18.1). Every release ships
|
|
123
|
+
* `dist/runtime/<slug>.js` so npm-installed users + marketplace cache
|
|
124
|
+
* users never need disk node_modules/react or Bun.build at request time.
|
|
125
|
+
* Read from disk → return.
|
|
126
|
+
* 2. **Dynamic Bun.build** — only fires for dev (cd dev-server; bun
|
|
127
|
+
* server.ts) where `dist/runtime/` may be empty/stale.
|
|
102
128
|
*/
|
|
103
|
-
export
|
|
129
|
+
export interface GetRuntimeBundleOptions {
|
|
130
|
+
/** Skip the disk cache lookup — used by build.ts to force a fresh dynamic build. */
|
|
131
|
+
skipPrebuilt?: boolean;
|
|
132
|
+
/** Minify the dynamic build output — used by build.ts in release mode. */
|
|
133
|
+
minify?: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function getRuntimeBundle(
|
|
137
|
+
pkg: RuntimePackage,
|
|
138
|
+
opts: GetRuntimeBundleOptions = {}
|
|
139
|
+
): Promise<BundleCacheEntry> {
|
|
104
140
|
const hit = cache.get(pkg);
|
|
105
|
-
if (hit) return hit;
|
|
141
|
+
if (hit && !opts.skipPrebuilt) return hit;
|
|
142
|
+
|
|
143
|
+
// (1) Pre-built bundle path — try disk first (unless caller opts out).
|
|
144
|
+
if (!opts.skipPrebuilt) {
|
|
145
|
+
const prebuilt = await loadPrebuiltRuntimeBundle(pkg);
|
|
146
|
+
if (prebuilt) {
|
|
147
|
+
cache.set(pkg, prebuilt);
|
|
148
|
+
return prebuilt;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
106
151
|
|
|
107
152
|
// A throwaway entrypoint that re-exports every member of the target package.
|
|
108
153
|
// We use named re-exports (default + an enumerated namespace) so the bundle
|
|
@@ -144,7 +189,7 @@ export async function getRuntimeBundle(pkg: RuntimePackage): Promise<BundleCache
|
|
|
144
189
|
entrypoints: [entryName],
|
|
145
190
|
target: 'browser',
|
|
146
191
|
format: 'esm',
|
|
147
|
-
minify: false,
|
|
192
|
+
minify: opts.minify ?? false,
|
|
148
193
|
splitting: false,
|
|
149
194
|
define: {
|
|
150
195
|
// Force React's production module (smaller, no dev-only `let React`
|
|
@@ -194,7 +239,12 @@ export async function getRuntimeBundle(pkg: RuntimePackage): Promise<BundleCache
|
|
|
194
239
|
return `[${lvl}] ${l.message}`;
|
|
195
240
|
})
|
|
196
241
|
.join('\n');
|
|
197
|
-
|
|
242
|
+
const remediation = bunCacheRemediation(pkg, msg);
|
|
243
|
+
throw new Error(
|
|
244
|
+
`Failed to build runtime bundle for "${pkg}":\n${msg || '(no log messages)'}${
|
|
245
|
+
remediation ? `\n\n${remediation}` : ''
|
|
246
|
+
}`
|
|
247
|
+
);
|
|
198
248
|
}
|
|
199
249
|
|
|
200
250
|
const out = built.outputs[0];
|
|
@@ -206,6 +256,29 @@ export async function getRuntimeBundle(pkg: RuntimePackage): Promise<BundleCache
|
|
|
206
256
|
return entry;
|
|
207
257
|
}
|
|
208
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Detect the "Bun's global install cache is in a bad state" failure mode and
|
|
261
|
+
* return a one-paragraph remediation message. Returns null when the build
|
|
262
|
+
* failure has a different shape (real syntax error, missing package, etc.) —
|
|
263
|
+
* the original log is enough then.
|
|
264
|
+
*
|
|
265
|
+
* Symptoms: log messages like `EISDIR reading '/Users/foo/.bun/install/cache/
|
|
266
|
+
* react@19.2.6@@@1 @@1/index.js'` or `ENOENT … .bun/install/cache/<pkg>@…`.
|
|
267
|
+
* Surfacing the cache path + the exact `bun pm cache rm <pkg>` command saves
|
|
268
|
+
* the user from grepping the error to figure out what to do. Phase 19 / DDR-044.
|
|
269
|
+
*/
|
|
270
|
+
export function bunCacheRemediation(pkg: string, log: string): string | null {
|
|
271
|
+
const cacheHit = /(EISDIR|ENOENT).*\.bun\/install\/cache\/([\w@/.-]+?)(?:@@@|\/)/i.test(log);
|
|
272
|
+
if (!cacheHit) return null;
|
|
273
|
+
const basePkg = pkg.split('/')[0] ?? pkg;
|
|
274
|
+
return [
|
|
275
|
+
` ⚠ Bun's global package cache for "${basePkg}" appears to be in a bad state`,
|
|
276
|
+
' (truncated install, EISDIR/ENOENT on an index file).',
|
|
277
|
+
'',
|
|
278
|
+
` Fix: run \`bun pm cache rm ${basePkg}\` then reload the page.`,
|
|
279
|
+
].join('\n');
|
|
280
|
+
}
|
|
281
|
+
|
|
209
282
|
function escapeRegex(s: string): string {
|
|
210
283
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
211
284
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { spawn } from 'node:child_process';
|
|
18
18
|
|
|
19
19
|
import { createApi } from './api.ts';
|
|
20
|
+
import { bootSelfHeal } from './boot-self-heal.ts';
|
|
20
21
|
import { createContext } from './context.ts';
|
|
21
22
|
import { createFsWatch } from './fs-watch.ts';
|
|
22
23
|
import { createHttp } from './http.ts';
|
|
@@ -24,6 +25,11 @@ import { createInspect } from './inspect.ts';
|
|
|
24
25
|
import { startHeapWatch } from './mem.ts';
|
|
25
26
|
import { createWs } from './ws.ts';
|
|
26
27
|
|
|
28
|
+
// Phase 19 / DDR-044 — covers the marketplace-cache-install gap where
|
|
29
|
+
// node_modules/ ships empty (git clone honors .gitignore). Auto-installs +
|
|
30
|
+
// builds on first boot; opt out with MAUDE_NO_AUTOBUILD=1.
|
|
31
|
+
await bootSelfHeal();
|
|
32
|
+
|
|
27
33
|
const ctx = createContext();
|
|
28
34
|
|
|
29
35
|
const api = createApi(ctx, async (file) => {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Boot artifact check — Phase 19 / DDR-044, simplified in v0.18.1.
|
|
2
|
+
// v0.18.0 tried to `bun install` + `bun run build.ts` when node_modules/react
|
|
3
|
+
// was missing; that misfired in every install scenario (see boot-self-heal.ts
|
|
4
|
+
// header). v0.18.1 ships pre-built runtime bundles + just verifies they're
|
|
5
|
+
// reachable. Missing artifact == broken install, not first-boot gap.
|
|
6
|
+
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
8
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
|
|
12
|
+
import { type SelfHealOptions, bootSelfHeal } from '../boot-self-heal.ts';
|
|
13
|
+
|
|
14
|
+
let TMP: string;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
TMP = mkdtempSync(join(tmpdir(), 'maude-self-heal-'));
|
|
18
|
+
mkdirSync(join(TMP, 'dist', 'runtime'), { recursive: true });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
rmSync(TMP, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function harness(extra: Partial<SelfHealOptions> = {}) {
|
|
26
|
+
const logs: string[] = [];
|
|
27
|
+
let exited: number | null = null;
|
|
28
|
+
const opts: SelfHealOptions = {
|
|
29
|
+
here: TMP,
|
|
30
|
+
log: (m) => logs.push(m),
|
|
31
|
+
exit: ((code: number) => {
|
|
32
|
+
exited = code;
|
|
33
|
+
throw new Error(`__exit:${code}`);
|
|
34
|
+
}) as never,
|
|
35
|
+
...extra,
|
|
36
|
+
};
|
|
37
|
+
return { opts, logs, getExited: () => exited };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function seedAll() {
|
|
41
|
+
writeFileSync(join(TMP, 'dist', 'client.bundle.js'), '/* stub */');
|
|
42
|
+
writeFileSync(join(TMP, 'dist', 'runtime', 'react.js'), '/* stub */');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe('bootSelfHeal', () => {
|
|
46
|
+
test('passes when both required artifacts present', async () => {
|
|
47
|
+
seedAll();
|
|
48
|
+
const { opts, logs } = harness();
|
|
49
|
+
const result = await bootSelfHeal(opts);
|
|
50
|
+
expect(result.verified).toEqual(['client.bundle.js', 'runtime/react.js']);
|
|
51
|
+
expect(logs).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('exits 1 with remediation when dist/client.bundle.js missing', async () => {
|
|
55
|
+
writeFileSync(join(TMP, 'dist', 'runtime', 'react.js'), '/* stub */');
|
|
56
|
+
const { opts, logs, getExited } = harness();
|
|
57
|
+
await expect(bootSelfHeal(opts)).rejects.toThrow('__exit:1');
|
|
58
|
+
expect(getExited()).toBe(1);
|
|
59
|
+
const msg = logs.join('\n');
|
|
60
|
+
expect(msg).toMatch(/dist\/client\.bundle\.js/);
|
|
61
|
+
expect(msg).toMatch(/npm uninstall -g @1agh\/maude/);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('exits 1 with remediation when dist/runtime/react.js missing', async () => {
|
|
65
|
+
writeFileSync(join(TMP, 'dist', 'client.bundle.js'), '/* stub */');
|
|
66
|
+
const { opts, logs, getExited } = harness();
|
|
67
|
+
await expect(bootSelfHeal(opts)).rejects.toThrow('__exit:1');
|
|
68
|
+
expect(getExited()).toBe(1);
|
|
69
|
+
const msg = logs.join('\n');
|
|
70
|
+
expect(msg).toMatch(/dist\/runtime\/react\.js/);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('lists ALL missing artifacts in one message (not first-fail-only)', async () => {
|
|
74
|
+
// Nothing seeded — both missing.
|
|
75
|
+
const { opts, logs } = harness();
|
|
76
|
+
await expect(bootSelfHeal(opts)).rejects.toThrow('__exit:1');
|
|
77
|
+
const msg = logs.join('\n');
|
|
78
|
+
expect(msg).toMatch(/dist\/client\.bundle\.js/);
|
|
79
|
+
expect(msg).toMatch(/dist\/runtime\/react\.js/);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('remediation surfaces the looked-under path so user can verify', async () => {
|
|
83
|
+
const { opts, logs } = harness();
|
|
84
|
+
await expect(bootSelfHeal(opts)).rejects.toThrow();
|
|
85
|
+
expect(logs.join('\n')).toContain(TMP);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// paths.ts — resolves real disk install root across dev mode, compiled npm
|
|
2
|
+
// install, and compiled marketplace install. Phase 19.1 / v0.18.1.
|
|
3
|
+
|
|
4
|
+
import { describe, expect, test } from 'bun:test';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
CLIENT_DIR,
|
|
8
|
+
DEV_SERVER_ROOT,
|
|
9
|
+
DIST_DIR,
|
|
10
|
+
IS_COMPILED_BINARY,
|
|
11
|
+
RUNTIME_BUNDLES_DIR,
|
|
12
|
+
} from '../paths.ts';
|
|
13
|
+
|
|
14
|
+
describe('paths.ts', () => {
|
|
15
|
+
test('DEV_SERVER_ROOT contains http.ts + package.json (canonical anchor)', () => {
|
|
16
|
+
expect(DEV_SERVER_ROOT).not.toMatch(/\$bunfs|~BUN/);
|
|
17
|
+
expect(Bun.file(`${DEV_SERVER_ROOT}/http.ts`).size).toBeGreaterThan(0);
|
|
18
|
+
expect(Bun.file(`${DEV_SERVER_ROOT}/package.json`).size).toBeGreaterThan(0);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('DIST_DIR + CLIENT_DIR + RUNTIME_BUNDLES_DIR are descendants of DEV_SERVER_ROOT', () => {
|
|
22
|
+
expect(DIST_DIR).toBe(`${DEV_SERVER_ROOT}/dist`);
|
|
23
|
+
expect(CLIENT_DIR).toBe(`${DEV_SERVER_ROOT}/client`);
|
|
24
|
+
expect(RUNTIME_BUNDLES_DIR).toBe(`${DEV_SERVER_ROOT}/dist/runtime`);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('IS_COMPILED_BINARY is false when running under bun directly (this test)', () => {
|
|
28
|
+
// bun:test invokes via `bun`, so import.meta.url is a real file:// path.
|
|
29
|
+
expect(IS_COMPILED_BINARY).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Phase 19 / DDR-044 — runtime-bundle error-message remediation.
|
|
2
|
+
// When Bun.build fails because the global install cache is corrupted
|
|
3
|
+
// (EISDIR/ENOENT on a cached package's entry file), surface an actionable
|
|
4
|
+
// `bun pm cache rm <pkg>` hint instead of just relaying the raw log.
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from 'bun:test';
|
|
7
|
+
|
|
8
|
+
import { bunCacheRemediation } from '../runtime-bundle.ts';
|
|
9
|
+
|
|
10
|
+
describe('bunCacheRemediation', () => {
|
|
11
|
+
test('returns null on unrelated build failures (real syntax errors etc.)', () => {
|
|
12
|
+
expect(bunCacheRemediation('react', '[error] Unexpected token <')).toBeNull();
|
|
13
|
+
expect(bunCacheRemediation('react-dom', '[error] Could not resolve "missing-pkg"')).toBeNull();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('matches EISDIR on .bun/install/cache + names the base package', () => {
|
|
17
|
+
const log =
|
|
18
|
+
"[error] EISDIR reading '/Users/iagh/.bun/install/cache/react@19.2.6@@@1 @@1/index.js'";
|
|
19
|
+
const out = bunCacheRemediation('react', log);
|
|
20
|
+
expect(out).not.toBeNull();
|
|
21
|
+
expect(out).toMatch(/bun pm cache rm react/);
|
|
22
|
+
expect(out).toMatch(/bad state/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('matches ENOENT on .bun/install/cache too', () => {
|
|
26
|
+
const log = "[error] ENOENT: '/Users/x/.bun/install/cache/react-dom@19.0.0/index.js'";
|
|
27
|
+
const out = bunCacheRemediation('react-dom', log);
|
|
28
|
+
expect(out).toMatch(/bun pm cache rm react-dom/);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('subpath specifiers strip to the base package for the cache rm command', () => {
|
|
32
|
+
// bun pm cache works on base package names — "react/jsx-runtime" → "react".
|
|
33
|
+
const log = "[error] EISDIR '/Users/x/.bun/install/cache/react@19.2.6/jsx-runtime.js'";
|
|
34
|
+
const out = bunCacheRemediation('react/jsx-runtime', log);
|
|
35
|
+
expect(out).toMatch(/bun pm cache rm react\b/);
|
|
36
|
+
expect(out).not.toMatch(/bun pm cache rm react\//);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('case-insensitive match (Bun has bounced casing across versions)', () => {
|
|
40
|
+
const log = '[error] eisdir reading /Users/x/.bun/install/cache/react@19/index.js';
|
|
41
|
+
expect(bunCacheRemediation('react', log)).not.toBeNull();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Authored under {{DS_NAME}}. Tokens + shared component classes load via the
|
|
13
13
|
* dev-server's _shell.html harness — link tags arrive automatically from the
|
|
14
|
-
* iframe's ?tokens= / ?components= query. Class names
|
|
15
|
-
* `_components.css`
|
|
16
|
-
* into a sibling .module.css iff
|
|
17
|
-
*
|
|
18
|
-
* one-off values.
|
|
14
|
+
* iframe's ?tokens= / ?components= query. Class names come from the DS's
|
|
15
|
+
* own `_components.css` — see `system/{{DS_NAME}}/preview/` for the available
|
|
16
|
+
* class shapes. Bespoke classes go into a sibling .module.css iff
|
|
17
|
+
* `css_mode === "modules"`; for `inline` prefer existing classes + `style={{}}`
|
|
18
|
+
* for arbitrary one-off values.
|
|
19
19
|
*
|
|
20
20
|
* The envelope primitives (`DesignCanvas`, `DCSection`, `DCArtboard`) and any
|
|
21
21
|
* specimen helpers come from the dev-server-bundled canvas library via the
|
|
@@ -40,11 +40,11 @@ export default function {{COMPONENT_NAME}}() {
|
|
|
40
40
|
<DesignCanvas>
|
|
41
41
|
<DCSection id="overview" title="{{SUBTITLE}}">
|
|
42
42
|
<DCArtboard id="primary" label="A · primary" width={1280} height={800}>
|
|
43
|
-
<div className="
|
|
43
|
+
<div className="{{ROOT_CLASS}}" data-theme="{{THEME_DEFAULT}}">
|
|
44
44
|
{/* Replace this scaffold with the actual canvas content. The
|
|
45
45
|
envelope this template was rendered against is in
|
|
46
46
|
{{HISTORY_DIR}}/000-envelope.md. */}
|
|
47
|
-
<h1
|
|
47
|
+
<h1>{{NAME}}</h1>
|
|
48
48
|
<p>{{BRIEF}}</p>
|
|
49
49
|
</div>
|
|
50
50
|
</DCArtboard>
|
package/plugins/design/templates/design-system-inspiration/audience-pro/colors-presence.html
CHANGED
|
@@ -4,7 +4,7 @@ DEMONSTRATES: --presence-online, --presence-away, --presence-offline; presence d
|
|
|
4
4
|
COMPOSITION: 4 avatars with presence dots (online / away / offline / idle) + a "live cursors" mockup with 3 simultaneous users
|
|
5
5
|
COPY VOICE: real names, real timestamps
|
|
6
6
|
WHEN SCAFFOLDED: presence family (IF "presence" ∈ activeFamilies — typically pro tools with multiplayer)
|
|
7
|
-
NOTES: Presence dot is 8px circle bottom-right of 32-40px avatars (anchor on the visual bottom-right, not on a math grid). Cursor labels are subtle (pill at 11px, low opacity bg). Don't blink presence — too noisy in a busy view.
|
|
7
|
+
NOTES: Presence dot is 8px circle bottom-right of 32-40px avatars (anchor on the visual bottom-right, not on a math grid). Per-user cursor colors below are illustrative only — the inline `oklch(...)` values are NOT part of the project's design system. A real implementation should source these from a `--presence-user-*` family or derive from `--accent` deterministically. Cursor labels are subtle (pill at 11px, low opacity bg). Don't blink presence — too noisy in a busy view.
|
|
8
8
|
-->
|
|
9
9
|
<!doctype html>
|
|
10
10
|
<html lang="en">
|
|
@@ -19,13 +19,15 @@
|
|
|
19
19
|
|
|
20
20
|
## Foundations
|
|
21
21
|
|
|
22
|
-
###
|
|
22
|
+
### Token contract
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
All visuals reference `var(--*)` tokens declared in `colors_and_type.css`. Adding a new visual concept means **extending the tokens CSS first**, never inventing values inline in a canvas.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
{{color_space_block}}
|
|
27
|
+
|
|
28
|
+
### Accent strategy
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
{{accent_rules_block}}
|
|
29
31
|
|
|
30
32
|
### Active token families
|
|
31
33
|
|
|
@@ -33,8 +35,9 @@ All visuals reference `var(--*)` tokens declared in `colors_and_type.css`. No ha
|
|
|
33
35
|
|
|
34
36
|
## Hard rules (non-negotiable)
|
|
35
37
|
|
|
36
|
-
- **Accessibility:** WCAG 2.1 AA contrast at every theme. Focus-visible always rendered.
|
|
37
|
-
- **
|
|
38
|
+
- **Accessibility:** WCAG 2.1 AA contrast at every theme. Focus-visible always rendered. `prefers-reduced-motion: reduce` respected.
|
|
39
|
+
- **Touch targets:** {{touch_target_rule}}
|
|
40
|
+
- **No off-token values** in canvases. Extend tokens; don't inline.
|
|
38
41
|
- **No placeholder copy.** Real product strings only — no "Lorem Solutions Inc.", no "Click here".
|
|
39
42
|
- **Type ladder:** {{type_scale_summary}}
|
|
40
43
|
- **Motion:** every animation uses a `var(--dur-*)` and `var(--ease-*)` token. No magic numbers.
|
|
@@ -52,7 +55,8 @@ All visuals reference `var(--*)` tokens declared in `colors_and_type.css`. No ha
|
|
|
52
55
|
## Hard-stops the completeness-critic enforces
|
|
53
56
|
|
|
54
57
|
- Core tokens present (`--accent`, `--bg-0..4`, `--fg-0..3`, `--dur-flip`)
|
|
55
|
-
-
|
|
58
|
+
- Accent family count matches the declared strategy ({{accent_strategy_summary}})
|
|
59
|
+
- Color space matches the declared choice ({{color_space_summary}})
|
|
56
60
|
- `system/{{ds_dirname}}/preview/` populated with at least 8 specimens
|
|
57
61
|
- `colors_and_type.css` linked from every specimen
|
|
58
62
|
- `prefers-reduced-motion: reduce` guard present in tokens CSS
|
|
@@ -24,8 +24,9 @@ It is **not** auto-invoked by `/design:edit` or `/design:new` on a project with
|
|
|
24
24
|
|
|
25
25
|
## What the agent should remember
|
|
26
26
|
|
|
27
|
-
- **
|
|
28
|
-
- **All visuals reference `var(--*)` tokens.** No
|
|
27
|
+
- **Accent strategy:** {{accent_strategy_summary}}. {{accent_rules_summary}}
|
|
28
|
+
- **All visuals reference `var(--*)` tokens.** No off-token values in canvases.
|
|
29
|
+
- **Color space:** {{color_space_summary}}.
|
|
29
30
|
- **Voice:** {{voice_tone_summary}}
|
|
30
31
|
- **Iconography:** {{iconography_summary}}
|
|
31
32
|
- **Theme default:** `{{theme_default}}`. {{theme_extra}}
|
|
@@ -47,4 +48,4 @@ It is **not** auto-invoked by `/design:edit` or `/design:new` on a project with
|
|
|
47
48
|
|
|
48
49
|
## How to extend
|
|
49
50
|
|
|
50
|
-
If a canvas iteration needs a value not currently in the system, **extend `colors_and_type.css` first**. Adding a new variant of an existing token (e.g. `--accent-tertiary`) is fine
|
|
51
|
+
If a canvas iteration needs a value not currently in the system, **extend `colors_and_type.css` first**. Adding a new variant of an existing token (e.g. `--accent-tertiary`) is fine. Adding a parallel accent family (e.g. `--accent2`) is only allowed if the project's declared `accentStrategy` in `config.json` permits it (e.g. `paired` or `chromatic-N`) — the completeness-critic enforces the declared strategy.
|