@polderlabs/bizar 4.4.8 → 4.4.9

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.
@@ -1,275 +1,147 @@
1
1
  ---
2
2
  name: glyph
3
- description: Create and consume Bizar glyphs — visual plan/recap artifacts in `artifacts/<slug>/`. Glyphs are MDX files with frontmatter and a block vocabulary. Use for design proposals, decision recaps, and postmortems.
4
- version: 2
3
+ description: Create and consume Bizar glyphs — visual artifacts at `artifacts/<slug>/`. Use for plans, recaps, design proposals, postmortems, handoffs. Quick reference for the block vocabulary and the 5 things that actually break a glyph.
4
+ version: 3
5
5
  ---
6
6
 
7
- # Glyphs — Visual Plan Artifacts
7
+ # Glyphs
8
8
 
9
- A **glyph** is a visual artifact used for plans, recaps, design proposals, and postmortems. It's an MDX file at `artifacts/<slug>/artifact.mdx` (or `~/.config/opencode/artifacts/<slug>/` for global glyphs) that the dashboard renders with comment-pin overlay and right-click context menu.
10
-
11
- ## When to create a glyph
12
-
13
- - **Design proposal** — you're proposing a new component, system, or workflow
14
- - **Decision recap** — multiple options were considered; one was chosen; the reasoning matters
15
- - **Postmortem** — something broke; here's the timeline and root cause
16
- - **Implementation plan** — breaking down a non-trivial feature into phases
17
- - **Handoff** — work moving from one agent to another
18
-
19
- If it's a one-line fix, don't make a glyph. If it's a 5-minute edit, don't make a glyph. Glyphs are for work that needs visible structure.
20
-
21
- ## File structure
9
+ A **glyph** is an MDX file the dashboard renders with a comment-pin overlay and right-click menu. Three files per glyph:
22
10
 
23
11
  ```
24
12
  artifacts/<slug>/
25
- ├── meta.json # title, slug, status, author, created, lastEdited
26
- ├── artifact.mdx # ← source of truth (git-tracked, diff-friendly)
27
- └── comments.json # ← free-placed pins (mutable, gitignored usually)
13
+ ├── meta.json title, status, author, created
14
+ ├── artifact.mdx ← source of truth
15
+ └── comments.json ← free-placed pins (mutable)
28
16
  ```
29
17
 
30
- Two locations are scanned:
31
- 1. **Per-project:** `<projectRoot>/.bizar/artifacts/<slug>/`
32
- 2. **Global:** `~/.config/opencode/artifacts/<slug>/`
33
-
34
- The dashboard uses whichever exists. The project's `artifacts/<slug>/` is preferred when running inside a worktree.
18
+ Two locations are scanned: `<projectRoot>/.bizar/artifacts/` (preferred) and `~/.config/opencode/artifacts/` (global).
35
19
 
36
20
  ## Frontmatter
37
21
 
38
22
  ```yaml
39
23
  ---
40
24
  title: "Short, action-oriented title"
41
- brief: "One sentence: what this glyph is about."
25
+ brief: "One sentence."
42
26
  status: draft | review | shipped | archived
43
27
  kind: plan | postmortem | recap | design
44
28
  ---
45
29
  ```
46
30
 
47
- The frontmatter is parsed once and exposed to the React renderer. Unknown keys are passed through. Status controls the badge color + whether the glyph is read-only.
48
-
49
- ## Block vocabulary
50
-
51
- ```mdx
52
- <RichText id="...">markdown content (headings, lists, code blocks, etc.)</RichText>
31
+ ## The blocks at a glance
53
32
 
54
- <Callout id="..." tone="info|warn|success|danger">markdown content</Callout>
33
+ | Block | What it shows | Required `data` |
34
+ | --- | --- | --- |
35
+ | `<RichText>` | Prose, lists, code blocks | none — body is markdown |
36
+ | `<Callout tone="info\|warn\|success\|danger">` | Boxed callout | none (tone defaults to info) |
37
+ | `<Stat label value trend hint />` | Big number with trend | `label`, `value` |
38
+ | `<Checklist items={[...]} />` | Checkbox list | `items: [{id,label,checked}]` |
39
+ | `<Table columns rows />` | Tabular data | `columns: []`, `rows: [[]]` |
40
+ | `<CodeTabs tabs />` | Tabbed code blocks | `tabs: [{id,label,language,code}]` |
41
+ | `<Decision title question options />` | Multi-option choice | `options: [{id,label,detail,recommended?}]` |
42
+ | `<OpenQuestions questions />` | Unanswered questions | `questions: [{id,label,kind,options?}]` |
43
+ | `<FileTree title entries />` | File-by-file change list | `entries: [{path,change,note?}]` |
44
+ | `<Diff before after filename language mode />` | Before/after diff | `before`, `after` |
45
+ | `<Workflow steps connections />` | Node graph | `steps: [{id,label,type}]` |
46
+ | `<Mockup title x y w h html />` | Inline UI mockup | `html` |
47
+ | `<Diagram title dataHtml dataCss />` | Inline SVG | `dataHtml` |
55
48
 
56
- <Stat id="..." label="..." value="..." trend="up|down|flat" hint="..." />
49
+ Every block needs a unique `id`. `RichText` and `Callout` use open/close tags with markdown children; everything else is self-closing.
57
50
 
58
- <Checklist
59
- id="..."
60
- items={[
61
- { id: "i1", label: "...", checked: true },
62
- { id: "i2", label: "...", checked: false }
63
- ]}
64
- />
51
+ ## Skeleton
65
52
 
66
- <Table
67
- id="..."
68
- columns={["A", "B"]}
69
- rows={[["x", "y"], ["p", "q"]]}
70
- />
53
+ ```mdx
54
+ <RichText id="overview">
55
+ ## What this covers
71
56
 
72
- <CodeTabs
73
- id="..."
74
- tabs={[
75
- { id: "t1", label: "file.ts", language: "typescript", code: "...", caption: "..." }
76
- ]}
77
- />
57
+ A one-paragraph summary.
58
+ </RichText>
78
59
 
79
- <Decision
80
- id="..."
81
- title="..."
82
- question="..."
83
- options={[
84
- { id: "a", label: "...", detail: "...", recommended: true }
85
- ]}
86
- />
60
+ <Stat id="headline" label="X" value="7" trend="flat" />
87
61
 
88
- <OpenQuestions
89
- id="..."
90
- questions={[
91
- { id: "q1", label: "...", kind: "choice|text|multi", options: ["A", "B"] }
92
- ]}
93
- />
62
+ <Callout id="warning" tone="warn">
63
+ One important thing.
64
+ </Callout>
94
65
 
95
66
  <FileTree
96
- id="..."
97
- title="..."
67
+ id="files"
68
+ title="Files changed"
98
69
  entries={[
99
- { path: "src/foo.ts", change: "added|modified|removed|renamed", note: "..." }
70
+ { path: "src/foo.ts", change: "modified", note: "why" },
71
+ { path: "src/bar.ts", change: "added" },
100
72
  ]}
101
73
  />
102
74
 
103
75
  <Workflow
104
- id="..."
76
+ id="flow"
105
77
  steps={[
106
- { id: "s1", label: "...", type: "task|decision|note" }
78
+ { id: "s1", label: "Step 1", type: "task" },
79
+ { id: "s2", label: "Step 2", type: "task" },
107
80
  ]}
108
81
  connections={[
109
- { from: "s1", to: "s2", label: "yes" }
82
+ { from: "s1", to: "s2" },
110
83
  ]}
111
84
  />
112
-
113
- <Mockup
114
- id="..."
115
- title="..."
116
- x={40} y={120} w={280} h={180}
117
- html="<div class='mockup-card'>...</div>"
118
- />
119
-
120
- <Diagram
121
- id="..."
122
- title="..."
123
- dataHtml="<svg>...</svg>"
124
- dataCss=".diagram-node { fill: var(--bg-1); }"
125
- />
126
85
  ```
127
86
 
128
- ## What data shape each block expects
129
-
130
- The dashboard validates every block's `data` shape BEFORE rendering. If a field is missing or the wrong type, the block renders an inline error card instead of crashing the whole canvas (v4.4.8+). The same thing happens if a block throws during render — a per-block `ErrorBoundary` catches it.
131
-
132
- | Block | Required `data` fields |
133
- | --- | --- |
134
- | `RichText` | none (children are markdown) |
135
- | `Callout` | none; optional `tone: 'info' \| 'warn' \| 'success' \| 'danger'` (default: info) |
136
- | `Stat` | `label: string`, `value: string \| number`; optional `trend`, `hint` |
137
- | `Checklist` | `items: Array<{ id, label, checked }>` |
138
- | `Table` | `columns: string[]`, `rows: string[][]` (each row is a cell array) |
139
- | `CodeTabs` | `tabs: Array<{ id, label, language, code, caption? }>` |
140
- | `Decision` | `options: Array<{ id, label, detail, recommended? }>`; optional `title`, `question` |
141
- | `OpenQuestions` | `questions: Array<{ id, label, kind, options? }>` |
142
- | `FileTree` | `entries: Array<{ path, change, note? }>` where `change ∈ {added, modified, removed, renamed}` |
143
- | `Diff` | `before: string`, `after: string`; optional `filename`, `language`, `mode` |
144
- | `Workflow` | `steps: Array<{ id, label, type }>`; optional `connections` |
145
- | `Mockup` | `html: string`; optional `title`, `x`, `y`, `w`, `h` |
146
- | `Diagram` | `dataHtml: string`; optional `title`, `dataCss` |
147
-
148
- ## Common pitfalls
87
+ ## The 5 things that break a glyph
149
88
 
150
- These are the bugs we've actually hit. Read this section before writing a glyph.
89
+ Read this BEFORE writing. Each one is a real bug we've shipped.
151
90
 
152
- ### 1. Backticks inside attribute strings break the parser
91
+ ### 1. Backticks inside attribute strings
153
92
 
154
- The MDX parser scans for `` ` `` to delimit template literals in JSX. A literal backtick inside an attribute value — even inside a double-quoted string will close the surrounding context and produce a parse error.
93
+ The parser scans for `` ` `` to delimit template literals. A literal backtick inside a string — even inside double-quotescloses the surrounding context and breaks parsing.
155
94
 
156
- ```mdx
157
- <!-- WRONG: \` inside a string value kills the parser -->
158
- <RichText id="x">Run \`bizar install\` to bootstrap.</RichText>
159
-
160
- <!-- RIGHT: use straight quotes or rephrase -->
161
- <RichText id="x">Run 'bizar install' to bootstrap.</RichText>
162
- <RichText id="x">Run the installer to bootstrap.</RichText>
163
95
  ```
164
-
165
- The parser's JSX-attribute walker does NOT track string literals when counting braces — so any `{` or `}` inside a string value also throws the parser off. Quote all strings that contain braces.
166
-
167
- ### 2. The parser counts braces naively (does NOT track strings)
168
-
169
- The `parseJsxValue` function uses a brace-depth counter that doesn't know about string literals. If you have something like:
170
-
171
- ```mdx
172
- <FileTree entries={[{ path: "x", change: "added", note: "this {weird} note" }]} />
96
+ WRONG: Run `bizar install` to bootstrap.
97
+ RIGHT: Run 'bizar install' to bootstrap.
98
+ RIGHT: Run the installer to bootstrap.
173
99
  ```
174
100
 
175
- …the inner `{` and `}` inside the string still increment the brace depth. With deeply-nested or brace-laden strings the walker terminates at the wrong closing brace, leaving a truncated value for `parseJsxValue`.
176
-
177
- **Rule of thumb:** keep string values brace-free. If you must use `{...}` in a note, escape or rephrase.
178
-
179
- ### 3. Trailing commas in arrays used to be a phantom-element bug
180
-
181
- Before v4.4.8, an array ending with a trailing comma — e.g. `[a, b,]` — caused the parser to call `parseValue` on the closing `]`, which fell through to the bareword-identifier fallback and returned `{__ident: ''}` as a phantom element. The FileTree then had one extra broken entry that crashed the React renderer (`t.bg` on undefined).
101
+ ### 2. The parser doesn't track strings when counting braces
182
102
 
183
- v4.4.8 fixed this in the parser. But: don't rely on trailing commas, even where supported keep the array clean.
103
+ `{` and `}` inside string values still increment the brace depth walker. With brace-laden strings the walker terminates at the wrong closing brace and the value gets truncated.
184
104
 
185
- ### 4. `FileTree` `change` field must be exact
105
+ **Rule:** keep string values brace-free. If you must use `{...}` in a note, escape or rephrase.
186
106
 
187
- `change` must be exactly one of: `added`, `modified`, `removed`, `renamed`. Anything else (including the empty string or `null`) makes `FILE_CHANGE[e.change]` return `undefined` and the renderer crashes with "can't access property 'bg' of undefined".
107
+ ### 3. `FileTree` `change` field must be exact
188
108
 
189
- ```mdx
190
- <!-- WRONG -->
191
- { path: "src/foo.ts", change: null, note: "..." }
192
- { path: "src/foo.ts", change: "", note: "..." }
109
+ `change` must be `added` | `modified` | `removed` | `renamed`. Anything else (empty string, `null`, typo) makes the renderer crash on `t.bg`.
193
110
 
194
- <!-- RIGHT -->
195
- { path: "src/foo.ts", change: "modified", note: "..." }
196
- { path: "src/foo.ts", change: "added", note: "..." }
197
111
  ```
198
-
199
- If a file's status is genuinely unclear, use `modified` with a `note` that explains why.
200
-
201
- ### 5. MDX heading with `<N`
202
-
203
- A heading like `## Mobile (under 768px)` is fine. But `## Mobile <768px` breaks the MDX validator because `<7` looks like a malformed JSX tag. Always escape or rephrase:
204
-
205
- ```mdx
206
- ## Mobile (under 768px) <!-- fine -->
207
- ## Mobile: under 768px <!-- fine -->
208
- ## Mobile &lt; 768px <!-- fine but ugly -->
112
+ WRONG: change: null, change: "", change: "Modified"
113
+ RIGHT: change: "modified"
209
114
  ```
210
115
 
211
- ### 6. Block IDs must be unique within a glyph
116
+ When in doubt, use `modified` with a `note` that explains why.
212
117
 
213
- If you copy a `<RichText>` block, change its `id`. The compiler may warn but won't fail. Two blocks with the same id will collide on the comment-pin overlay.
118
+ ### 4. Two blocks with the same id
214
119
 
215
- ### 7. Children vs self-closing
120
+ Comment pins and error reporting are keyed by block id. Duplicates collide. Each `id` must be unique within a glyph.
216
121
 
217
- Blocks with content (`RichText`, `Callout`) use open/close tags with children. Blocks with only data (`Stat`, `Table`, `FileTree`, `Decision`, etc.) are self-closing — no children, no closing tag.
218
-
219
- ```mdx
220
- <!-- self-closing -->
221
- <Stat id="x" label="..." value="..." />
122
+ ### 5. MDX heading with `<N`
222
123
 
223
- <!-- with children -->
224
- <RichText id="x">markdown content here</RichText>
225
- <Callout id="x" tone="warn">important message</Callout>
226
124
  ```
227
-
228
- ### 8. Em-dashes and special characters
229
-
230
- The parser's JSON-like value reader handles `\\`, `\"`, `\'`, `\n`, `\t`, `\r` inside string literals. Em-dashes (`—`), curly quotes (`""`), and other Unicode characters are fine. Just avoid `\``.
231
-
232
- ## meta.json example
233
-
234
- ```json
235
- {
236
- "title": "Chat UI — Complete Rewrite (Gemini-Inspired)",
237
- "slug": "chat-ui-rewrite",
238
- "status": "draft",
239
- "author": "drb0rk",
240
- "created": "2026-06-26T20:30:00.000Z",
241
- "lastEdited": "2026-06-26T20:30:00.000Z"
242
- }
125
+ WRONG: ## Mobile <768px ← <7 looks like a malformed tag
126
+ RIGHT: ## Mobile (under 768px)
127
+ RIGHT: ## Mobile: under 768px
243
128
  ```
244
129
 
245
- `lastEdited` is updated whenever the dashboard re-saves the glyph via the toolbar.
130
+ ## Validate before shipping
246
131
 
247
- ## Validation workflow
248
-
249
- Before committing a glyph:
250
-
251
- 1. Run the compiler against your MDX:
252
- ```bash
253
- node -e "import('./bizar-dash/src/server/glyphs/mdx-compiler.mjs').then(m => { const fs = require('node:fs'); const src = fs.readFileSync('artifacts/<slug>/artifact.mdx', 'utf8'); const out = m.compileGlyphMdxSync(src); console.log('blocks:', out.blocks.length, 'errors:', out.errors); out.errors.forEach(e => console.error(' line', e.line, ':', e.message)); })"
254
- ```
255
- This catches parser bugs (missing tags, malformed attrs, etc.) before they hit the dashboard.
256
-
257
- 2. Visually inspect the rendered output by hitting `GET /api/artifacts/<slug>/render`. The response is the compiled JSON the React renderer consumes. Look for blocks with missing data fields — those will show up as `data: {}` or `data: []`.
258
-
259
- 3. Open the glyph in the dashboard. v4.4.8+ shows a red error banner at the top of the canvas if any block failed to render. The banner lists every block error AND every compiler warning.
260
-
261
- ## How to view in the dashboard
132
+ ```bash
133
+ node -e "import('./bizar-dash/src/server/glyphs/mdx-compiler.mjs').then(m => { \
134
+ const fs = require('node:fs'); \
135
+ const out = m.compileGlyphMdxSync(fs.readFileSync('artifacts/<slug>/artifact.mdx','utf8')); \
136
+ console.log('blocks:', out.blocks.length, 'errors:', out.errors.length); \
137
+ out.errors.forEach(e => console.error(' line', e.line, ':', e.message)); \
138
+ })"
139
+ ```
262
140
 
263
- Glyphs are rendered at `/artifacts/<slug>`. The dashboard shows them with:
264
- - **Section grouping** — by block id prefix (e.g. all blocks with ids starting `overview_` go into the "Overview" section)
265
- - **Comment pin overlay** — right-click anywhere to add a comment
266
- - **Floating toolbar** — Send to agent, share, fullscreen
267
- - **Error banner** — v4.4.8+: red banner at the top with block render errors and compiler warnings. The page is no longer blank when something fails — you'll see exactly which block crashed and why.
268
- - **Status badge** — `draft | review | shipped | archived`. `shipped` locks editing.
141
+ If errors > 0, fix the MDX before opening the glyph in the browser. When a block DOES fail in the browser, v4.4.8+ shows a red banner with block id + error message — no more blank screen.
269
142
 
270
143
  ## See also
271
144
 
272
- - `bizar-dash/src/web/views/glyphs/components.tsx` — block component implementations
273
- - `bizar-dash/src/server/glyphs/mdx-compiler.mjs` — MDX parser (custom, no @mdx-js dependency at runtime)
274
- - `bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx` — React renderer (v4.4.8+ has the error banner + ErrorBoundary)
275
- - `artifacts/sample-plan-login/` — a complete worked example
145
+ - `bizar-dash/src/server/glyphs/mdx-compiler.mjs` — parser
146
+ - `bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx` — React renderer (has the error banner)
147
+ - `bizar-dash/src/web/views/glyphs/components.tsx` — block component implementations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "4.4.8",
3
+ "version": "4.4.9",
4
4
  "description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
5
5
  "type": "module",
6
6
  "bin": {