@ammduncan/easel 0.2.28 → 0.2.29
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/CHANGELOG.md +5 -0
- package/dist/client/viewer.js +32 -6
- package/dist/mcp.js +1 -1
- package/package.json +1 -1
- package/skills/using-easel/SKILL.md +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to easel. This project adheres to [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 0.2.29 — 2026-05-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **`.window` mockups no longer wash out in a dark-mode viewer — pin a stable surface like `.code`/`.terminal` already do.** `.window` set `background: light-dark(#ffffff, #161616)`, so its surface *flipped* with the host theme, and it never pinned text color or re-scoped `color: inherit` to children. In a dark-mode viewer the window resolved to a dark `#161616` panel, and a light-dashboard mockup's subtle gray-on-white labels (pills, captions, KPI sublabels) vanished against it — only explicitly-coloured figures survived. This is the identical surface-vs-ink mismatch the 0.2.28 `.code`/`.terminal` primitive locks against; it was fixed for code blocks but the `.window` chrome was never given the same treatment, and its theme-flipping background made it worse (the same mockup rendered differently per viewer). A mockup renders an app's own UI and should look the same to everyone, so `.window` is now a **stable light canvas**: white bg, pinned `#1a1a1a` ink, `color: inherit` re-scoped to every child. Added an opt-in **`.window.dark`** variant (locked dark surface + light ink + dark chrome + stronger shadow) for genuinely dark-UI mockups, and a `@media print` override so a dark mockup prints legibly (browsers drop background colors by default, which would otherwise strand its light ink on white paper). Skill + `push` tool description updated to document the stable surface and `.window.dark`.
|
|
9
|
+
|
|
5
10
|
## 0.2.28 — 2026-05-25
|
|
6
11
|
|
|
7
12
|
### Added
|
package/dist/client/viewer.js
CHANGED
|
@@ -728,16 +728,26 @@ body > *:last-child { margin-bottom: 0 !important; }
|
|
|
728
728
|
Add the desktop class for a full desktop-screen canvas — min-height 900px,
|
|
729
729
|
i.e. the 1440x900 (16:10) standard design canvas — so a screen mockup looks
|
|
730
730
|
like a real window with viewport breathing room rather than a short strip.
|
|
731
|
-
Omit desktop for dialogs / small components so they stay content-sized.
|
|
731
|
+
Omit desktop for dialogs / small components so they stay content-sized.
|
|
732
|
+
|
|
733
|
+
A mockup renders an app's own UI, so it owns a STABLE surface that does NOT
|
|
734
|
+
flip with the host theme — otherwise a light dashboard mockup renders on a dark
|
|
735
|
+
window in a dark-mode viewer and every subtle gray label washes out (same
|
|
736
|
+
surface-vs-ink mismatch the .code primitive locks against). Default is a light
|
|
737
|
+
canvas with pinned dark ink and color:inherit re-scoped to every child so the
|
|
738
|
+
host's light-dark() ink can never leak in. Add the dark class
|
|
739
|
+
(class="window dark") for a genuinely dark-UI mockup. */
|
|
732
740
|
.window {
|
|
733
741
|
position: relative;
|
|
734
742
|
padding-top: 40px;
|
|
735
743
|
border-radius: 12px;
|
|
736
|
-
border: 1px solid
|
|
744
|
+
border: 1px solid #e2e2e2;
|
|
737
745
|
box-shadow: 0 14px 48px rgba(0, 0, 0, 0.16);
|
|
738
746
|
overflow: hidden;
|
|
739
|
-
background:
|
|
747
|
+
background: #ffffff;
|
|
748
|
+
color: #1a1a1a;
|
|
740
749
|
}
|
|
750
|
+
.window * { color: inherit; }
|
|
741
751
|
.window::before {
|
|
742
752
|
content: "";
|
|
743
753
|
position: absolute;
|
|
@@ -745,8 +755,8 @@ body > *:last-child { margin-bottom: 0 !important; }
|
|
|
745
755
|
left: 0;
|
|
746
756
|
right: 0;
|
|
747
757
|
height: 40px;
|
|
748
|
-
background-color:
|
|
749
|
-
border-bottom: 1px solid
|
|
758
|
+
background-color: #f1f1f1;
|
|
759
|
+
border-bottom: 1px solid #e2e2e2;
|
|
750
760
|
background-image:
|
|
751
761
|
radial-gradient(circle at 19px 20px, #ff5f57 6px, transparent 6.5px),
|
|
752
762
|
radial-gradient(circle at 39px 20px, #febc2e 6px, transparent 6.5px),
|
|
@@ -765,9 +775,20 @@ body > *:last-child { margin-bottom: 0 !important; }
|
|
|
765
775
|
justify-content: center;
|
|
766
776
|
font-size: 13px;
|
|
767
777
|
font-weight: 500;
|
|
768
|
-
color:
|
|
778
|
+
color: #6b6b6b;
|
|
769
779
|
pointer-events: none;
|
|
770
780
|
}
|
|
781
|
+
.window.dark {
|
|
782
|
+
border-color: #2a2a2a;
|
|
783
|
+
background: #161616;
|
|
784
|
+
color: #e6edf3;
|
|
785
|
+
box-shadow: 0 14px 48px rgba(0, 0, 0, 0.4);
|
|
786
|
+
}
|
|
787
|
+
.window.dark::before {
|
|
788
|
+
background-color: #1f1f1f;
|
|
789
|
+
border-bottom-color: #2a2a2a;
|
|
790
|
+
}
|
|
791
|
+
.window.dark::after { color: #9b9b9b; }
|
|
771
792
|
.window.desktop {
|
|
772
793
|
min-height: 900px;
|
|
773
794
|
}
|
|
@@ -914,6 +935,11 @@ img { max-width: 100%; height: auto; border-radius: 10px; }
|
|
|
914
935
|
body > h1, body > h2, body > h3, body > h4 { max-width: none !important; }
|
|
915
936
|
pre, code, .code, .terminal { background: #f4f3ed !important; color: #111 !important; border: 1px solid #ddd; }
|
|
916
937
|
.code *, .terminal * { color: #111 !important; }
|
|
938
|
+
/* Force the dark window variant light for print — browsers drop background
|
|
939
|
+
colors by default, which would leave its light ink invisible on white paper
|
|
940
|
+
(same reason .code/.terminal are forced light above). */
|
|
941
|
+
.window.dark { background: #ffffff !important; color: #111 !important; }
|
|
942
|
+
.window.dark * { color: #111 !important; }
|
|
917
943
|
.card, .panel { background: #fff !important; border: 1px solid #ddd !important; box-shadow: none !important; }
|
|
918
944
|
a { color: #111 !important; text-decoration: underline; border-bottom: 0 !important; }
|
|
919
945
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -145,7 +145,7 @@ export async function main() {
|
|
|
145
145
|
"• Stack desktop mockups VERTICALLY with labels ('Now', 'Proposed') — don't squeeze them side-by-side. The iframe is ~900px wide; two desktop screens at half-width crush columns, wrap headings to 3 lines, and turn tables unreadable.\n" +
|
|
146
146
|
"• Side-by-side is fine only for narrow mobile mockups, small cards, or short text columns that genuinely fit in half-width.\n" +
|
|
147
147
|
"• Mockup embedded mid-explanation? Prose is left-aligned and capped ~880px; wrap JUST the mockup in <div class=\"full-bleed\">…</div> and it fills the content column from the SAME left edge (wider than the prose, sharing one left margin; the body padding stays as a gutter so nothing touches the card edge). (If the WHOLE push is a UI recreation, use kind:'mockup'/'app' instead.)\n" +
|
|
148
|
-
"• Window chrome: wrap a mockup in <div class=\"window\" data-title=\"App name\">…</div> for a macOS window frame (title bar + red/yellow/green traffic-light dots + centred title). Add the `desktop` class (class=\"window desktop\") for the 1440x900 (16:10) desktop canvas via min-height:900px; omit it so dialogs/components size to content. Combine with .full-bleed to fill the column. NOTE: .window sets overflow:hidden (to clip its rounded corners) — so NEVER put a fixed `height` on .window or any inner stage, or content past that height is silently guillotined. It's built to grow via min-height.\n" +
|
|
148
|
+
"• Window chrome: wrap a mockup in <div class=\"window\" data-title=\"App name\">…</div> for a macOS window frame (title bar + red/yellow/green traffic-light dots + centred title). Add the `desktop` class (class=\"window desktop\") for the 1440x900 (16:10) desktop canvas via min-height:900px; omit it so dialogs/components size to content. Combine with .full-bleed to fill the column. .window is a STABLE LIGHT canvas — it pins white bg + dark ink + re-scopes color:inherit to children (like .code/.terminal), so it does NOT flip with the host theme and subtle gray-on-white labels stay legible even in a dark-mode viewer; a mockup is a screenshot, it should look the same to everyone. For a genuinely dark-UI mockup add the `dark` class (class=\"window dark\") — don't hand-roll a dark .window. NOTE: .window sets overflow:hidden (to clip its rounded corners) — so NEVER put a fixed `height` on .window or any inner stage, or content past that height is silently guillotined. It's built to grow via min-height.\n" +
|
|
149
149
|
"• BUILD MOCKUPS FLUID, not fixed-width. Lay the inside out with flex / % / fr widths, not hardcoded width:1440px columns. 1440 is a MAX, not a target. A fluid mockup reflows to fit when the viewer's window is squeezed — no horizontal scroll, nothing clipped, exports stay complete. A fixed-pixel-width mockup gets cut off or needs an awkward horizontal scrollbar when narrowed.\n" +
|
|
150
150
|
"• NEVER CLIP CONTENT — no fixed `height` + `overflow:hidden` on any container that holds content (cards, panels, device/browser/phone frames, stages, slideovers, toasts). That combo guillotines anything taller than your guessed height — buttons sliced through text, lists cut mid-item. Containers size to their CONTENT: use `min-height` for a floor, NEVER a fixed `height`. `overflow:hidden` is allowed ONLY for genuine cosmetic crops where clipping IS the intent (rounded-corner image masks, decorative bleed) — never on a content region. Decorative frames must grow with their content. When unsure, leave height unset. Mentally render the tallest card: if any text/button could exceed the box, the box is wrong.\n" +
|
|
151
151
|
"• MATCH THE SOURCE'S REAL FRAME — faithful height, not minimal, in both directions. Mocking a COMPONENT (card, modal, row, toolbar)? Size to content — do NOT pad with min-height:560px to feel 'desktop-y'; that floats content in dead whitespace. Mocking a FULL DESKTOP SCREEN (login page, dashboard)? Give it realistic viewport proportions via MIN-HEIGHT (e.g. min-height:760px or a 16:10 floor — never a fixed `height`, which clips) and lay content out inside as the real screen does (centred form, top nav). Either way copy the source's exact dimensions if it has them, as a min-height. Test: cropped the same way, would your mock look like a screenshot of the real thing? Empty bands = over-padded; a screen squashed to a strip = under-sized.\n" +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ammduncan/easel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.29",
|
|
4
4
|
"description": "A live browser tab for every Claude Code (and MCP) session. The push MCP tool appends HTML cards to a scrolling feed you keep open in split-screen.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -317,6 +317,8 @@ Wrap a mockup in `.window` to give it a macOS window frame — a title bar with
|
|
|
317
317
|
- `data-title` sets the centred title text (omit for a blank bar).
|
|
318
318
|
- Add the **`desktop`** class for a full desktop-screen canvas — `min-height: 900px`, the standard 1440×900 (16:10) design canvas — so a screen mockup looks like a real window with viewport breathing room. **Omit `desktop`** for a dialog or small component so the chrome sizes to its content (don't pad a small thing to screen height).
|
|
319
319
|
|
|
320
|
+
**`.window` is a stable LIGHT canvas — it does not flip with the host theme.** A mockup is a screenshot of an app; it should look the same to every viewer regardless of their OS/viewer mode. So `.window` pins a white surface with dark ink and re-scopes `color: inherit` to children (same locked-surface treatment as `.code`/`.terminal`) — you can use subtle gray-on-white labels inside and they stay legible even when the viewer is in dark mode. For a genuinely **dark-UI** mockup, add the **`dark`** class (`class="window dark"`) — it locks a dark surface + light ink + dark chrome instead. Don't hand-roll a dark `.window` with inline backgrounds.
|
|
321
|
+
|
|
320
322
|
**Build the mockup fluid, not fixed-width.** Lay the inside out with flex / `%` / `fr` widths, not hardcoded `width: 1440px` columns. The content column caps at a desktop-realistic width, but when the viewer's window is narrower (a "squeezed" screen) a fluid mockup simply **reflows to fit** — no horizontal scroll, nothing clipped, and PNG/PDF export still captures the whole thing. A fixed-pixel-width mockup gets cut off or needs an awkward horizontal scrollbar when squeezed; a fluid one never does. The 1440 is a *max*, not a target.
|
|
321
323
|
|
|
322
324
|
**`.window` sets `overflow: hidden`** (to clip its own rounded corners) and grows via `min-height`, never a fixed height. So **never put a fixed `height` on `.window` itself, or on an inner "stage" element** — content past that height gets silently guillotined, and because the overflow is on the frame the crop is invisible until you export or scroll. Let it grow.
|