Skip to content

Commit b013baa

Browse files
committed
fix(gallery): icon squircle compression + JSON tab
Icon fix The 110x110 icon cell wasn't tall enough for a 78px squircle + 8px gap + 14px name label (~100px content needed). The flexbox shrunk the squircle to 78x62 — squashed. Two fixes: - Reduce squircle to 64x64 with 18px radius (still iOS-shaped) so it fits in the 110x110 cell with the name label - Add flex-shrink: 0 to .ws-app-tile so it can NEVER be compressed by its container — defense against this whole class of bug - Mosaic tiles override back to 78x78 (they have more breathing room) Verified via Playwright: icon cell 110x110, squircle 64x64 exact. JSON tab Third tab next to HTML and Markdown. Shows the raw WidgetData JSON the author returned — the canonical primitive in v0.3+. Useful for people understanding the architecture: 'this is the JSON the framework converts to the other formats.' Implementation - ViewDef now has optional toJSON(state, opts) → WidgetData | null (null for legacy escape-hatch views) - defineView with sizes builder synthesizes toJSON automatically - Gallery cell renders all three formats; CSS shows one based on size-strip[data-format='json'] etc. - New .cell-json div with monospace styling Tests: 30/30 still green.
1 parent f4b1961 commit b013baa

3 files changed

Lines changed: 926 additions & 28 deletions

File tree

gallery/build.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ interface SizeRender {
2626
size: ViewSize;
2727
html: string;
2828
markdown: string;
29-
tokens: number; // rough: words / 0.75
29+
json: string; // pretty-printed WidgetData (or "" if legacy)
30+
tokens: number; // rough: markdown words / 0.75
3031
}
3132

3233
interface Card {
@@ -50,7 +51,9 @@ function buildCards(group: Record<string, AssetDef<any>>): Card[] {
5051
const renders: SizeRender[] = SIZES.map(size => {
5152
const html = asset.defaultView.toHTML(state, { size });
5253
const markdown = asset.defaultView.toMarkdown(state, { size });
53-
return { size, html, markdown, tokens: approxTokens(markdown) };
54+
const widget = asset.defaultView.toJSON?.(state, { size });
55+
const json = widget ? JSON.stringify(widget, null, 2) : "";
56+
return { size, html, markdown, json, tokens: approxTokens(markdown) };
5457
});
5558
const icon = renders.find(r => r.size === "icon")!;
5659
out.push({
@@ -108,33 +111,36 @@ main { max-width: 1400px; margin: 0 auto; padding: 32px 24px 80px; }
108111
.mosaic-tile .ws-app-name { color: rgba(255,255,255,0.85); }
109112
110113
/* ----- App icon (size=icon) ----- */
111-
.ws-app-icon { display: flex; flex-direction: column; align-items: center; gap: 8px; }
114+
.ws-app-icon { display: flex; flex-direction: column; align-items: center; gap: 6px; }
112115
.ws-app-tile {
113116
position: relative;
114-
width: 78px;
115-
height: 78px;
117+
width: 64px;
118+
height: 64px;
119+
flex-shrink: 0; /* never compress — bug bait otherwise */
116120
/* iOS squircle approximation — superellipse-ish via large border-radius */
117-
border-radius: 22px;
121+
border-radius: 18px;
118122
display: flex;
119123
align-items: center;
120124
justify-content: center;
121125
box-shadow: 0 6px 20px rgba(0,0,0,0.25), 0 1px 2px rgba(255,255,255,0.15) inset;
122126
transition: transform 0.12s ease;
123127
}
128+
.mosaic-tile { display: block; text-decoration: none; }
129+
.mosaic-tile .ws-app-tile { width: 78px; height: 78px; border-radius: 22px; }
124130
.mosaic-tile:hover .ws-app-tile { transform: translateY(-2px); }
125131
.ws-app-tile--chip { width: 28px; height: 28px; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.15); }
126-
.ws-app-glyph { width: 44%; height: 44%; display: flex; align-items: center; justify-content: center; }
132+
.ws-app-glyph { width: 50%; height: 50%; display: flex; align-items: center; justify-content: center; }
127133
.ws-app-glyph svg { width: 100%; height: 100%; }
128134
.ws-app-tile--chip .ws-app-glyph { width: 60%; height: 60%; }
129-
.ws-app-name { font-size: 12px; color: var(--fg); font-weight: 500; line-height: 1.2; }
135+
.ws-app-name { font-size: 11px; color: var(--fg); font-weight: 500; line-height: 1.2; flex-shrink: 0; }
130136
.ws-app-badge {
131137
position: absolute; top: -4px; right: -4px;
132-
min-width: 22px; height: 22px;
133-
padding: 0 6px;
138+
min-width: 20px; height: 20px;
139+
padding: 0 5px;
134140
border-radius: 999px;
135141
background: #ff3b30;
136142
color: white;
137-
font-size: 12px;
143+
font-size: 11px;
138144
font-weight: 600;
139145
display: flex; align-items: center; justify-content: center;
140146
border: 2px solid #fff;
@@ -217,14 +223,14 @@ main { max-width: 1400px; margin: 0 auto; padding: 32px 24px 80px; }
217223
.size-cell.small { aspect-ratio: 1; } /* 2×2 — square */
218224
.size-cell.medium { aspect-ratio: 2 / 1; } /* 4×2 — wide */
219225
.size-cell.large { aspect-ratio: 1; } /* 4×4 — square */
220-
.cell-html, .cell-md {
226+
.cell-html, .cell-md, .cell-json {
221227
position: absolute; inset: 12px;
222228
overflow: hidden;
223229
}
224230
.cell-html { display: flex; align-items: center; justify-content: center; }
225231
.cell-html > * { width: 100%; max-height: 100%; overflow: hidden; }
226232
.size-cell.icon .cell-html > * { width: auto; }
227-
.cell-md {
233+
.cell-md, .cell-json {
228234
display: none;
229235
background: white;
230236
border: 1px solid var(--border);
@@ -236,8 +242,11 @@ main { max-width: 1400px; margin: 0 auto; padding: 32px 24px 80px; }
236242
color: var(--fg);
237243
overflow: auto;
238244
}
239-
.size-strip[data-format="markdown"] .cell-html { display: none; }
240-
.size-strip[data-format="markdown"] .cell-md { display: block; }
245+
.cell-json { color: #475569; }
246+
.size-strip[data-format="markdown"] .cell-html { display: none; }
247+
.size-strip[data-format="markdown"] .cell-md { display: block; }
248+
.size-strip[data-format="json"] .cell-html { display: none; }
249+
.size-strip[data-format="json"] .cell-json { display: block; }
241250
.card { background: var(--card-bg); border: 1px solid var(--border); border-radius: 12px; margin-bottom: 28px; overflow: hidden; }
242251
.card-head { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
243252
.card-name { font-size: 18px; font-weight: 600; }
@@ -364,6 +373,7 @@ const sizeCell = (r: SizeRender) => `
364373
<div class="size-cell ${r.size}">
365374
<div class="cell-html">${r.html}</div>
366375
<div class="cell-md">${mdSyntax(r.markdown)}</div>
376+
<div class="cell-json">${escapeHtml(r.json || "// legacy view (no JSON form)")}</div>
367377
</div>
368378
</div>
369379
`;
@@ -401,6 +411,7 @@ const cardHtml = (c: Card) => {
401411
<div class="format-tabs" data-tabs>
402412
<button data-format="html" class="active">HTML</button>
403413
<button data-format="markdown">Markdown</button>
414+
<button data-format="json">JSON</button>
404415
</div>
405416
<div class="size-strip" data-format="html">
406417
${c.renders.map(sizeCell).join("")}

0 commit comments

Comments
 (0)