@the-portland-company/canvas-editor 0.2.0 → 0.3.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/CHANGELOG.md +39 -0
- package/README.md +12 -0
- package/dist/canvas.css +486 -0
- package/dist/chunk-6XOUU7UE.js +727 -0
- package/dist/chunk-6XOUU7UE.js.map +1 -0
- package/dist/chunk-XQ4AURVN.js +637 -0
- package/dist/chunk-XQ4AURVN.js.map +1 -0
- package/dist/editor.cjs +3000 -550
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +214 -7
- package/dist/editor.d.ts +214 -7
- package/dist/editor.js +2415 -414
- package/dist/editor.js.map +1 -1
- package/dist/index-Cxk71DVp.d.ts +114 -0
- package/dist/index-S2hjr-C4.d.cts +114 -0
- package/dist/index.cjs +683 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -138
- package/dist/index.d.ts +66 -138
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/registry-BO8AfpUv.d.ts +302 -0
- package/dist/registry-B_eYgKbr.d.cts +302 -0
- package/dist/renderer.cjs +299 -30
- package/dist/renderer.cjs.map +1 -1
- package/dist/renderer.d.cts +2 -22
- package/dist/renderer.d.ts +2 -22
- package/dist/renderer.js +1 -2
- package/dist/{types-BnMorrOZ.d.cts → types-GmfMPpfA.d.cts} +11 -4
- package/dist/{types-BnMorrOZ.d.ts → types-GmfMPpfA.d.ts} +11 -4
- package/package.json +21 -1
- package/dist/adapters-BNNgdXGf.d.ts +0 -131
- package/dist/adapters-Fge5XZ5R.d.cts +0 -131
- package/dist/chunk-F4XERUOO.js +0 -252
- package/dist/chunk-F4XERUOO.js.map +0 -1
- package/dist/chunk-RQHKLW3X.js +0 -211
- package/dist/chunk-RQHKLW3X.js.map +0 -1
- package/dist/chunk-XE353SZL.js +0 -279
- package/dist/chunk-XE353SZL.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 — feature + styling parity with the main VRM app's email editor
|
|
4
|
+
|
|
5
|
+
Per Spencer's 2026-09-11 direction: bring the shared editor to parity with the
|
|
6
|
+
app.politogyvrm.com email editor. Gap analysis in `docs/APP_EDITOR_PARITY_MANIFEST.md`.
|
|
7
|
+
|
|
8
|
+
- **Rich text is now tiptap v3** (`CanvasRichText`): 42-stack font-family menu
|
|
9
|
+
with Email-safe / Not-email-safe badges and the brand font first, 10-step
|
|
10
|
+
font sizes, line-spacing (Single/1.5/Double), text color, B/I/U, align incl.
|
|
11
|
+
justify, lists, link popover (no `window.prompt`), heading levels P/H1–H6,
|
|
12
|
+
emoji picker, personalization tokens at the cursor. Sanitizer now keeps
|
|
13
|
+
`h5/h6`, inline `line-height`, and heading/list `style`.
|
|
14
|
+
**New peer deps:** `@tiptap/{core,react,starter-kit,extension-underline,
|
|
15
|
+
extension-text-align,extension-link,extension-text-style,extension-color,
|
|
16
|
+
extension-font-family}` ^3.27 and `emoji-picker-react` ^4.19.
|
|
17
|
+
- **New block types:** `hero`, `card`, `container`, `pdf`, `html` (sanitized,
|
|
18
|
+
code/visual modes); `column` gains `distribution` + `gap`; `section` gains
|
|
19
|
+
`backgroundImage` / `borderRadius` / `columnPadding`; social icons gain
|
|
20
|
+
`style` solid|outline|minimal and `colorScheme` brand|black|white|custom.
|
|
21
|
+
Footer derives defaults from `BrandInfo.org` with cleared-vs-unset semantics
|
|
22
|
+
and is enforced singleton + last via `normalizeFooter`.
|
|
23
|
+
- **Padding presets** S/M/L/Custom per block kind (`blocks/padding.ts`,
|
|
24
|
+
`PaddingFields presetKind`), with shorthand ⇄ `{t,r,b,l}` converters.
|
|
25
|
+
- **Undo / redo** (depth 50) with ⌘Z / ⌘⇧Z / Ctrl+Y and TopBar buttons.
|
|
26
|
+
- **Insert model:** animated exact-slot drop zones between blocks (root +
|
|
27
|
+
nested), type-sized drag ghost, grouped Basic / Layout insert menu, `side`
|
|
28
|
+
gutter mode via the new `insertMode` prop; `pointerWithin` collision with a
|
|
29
|
+
`closestCenter` fallback.
|
|
30
|
+
- **Canvas settings:** width Fixed/Fluid, content width px, top/bottom padding
|
|
31
|
+
(new nullable `EmailSettings` fields, honored by canvas + renderer).
|
|
32
|
+
- **Draft safety:** draft signature skips no-op saves; stale-read reseed guard
|
|
33
|
+
ignores an older host document.
|
|
34
|
+
- **Modals + toasts** replace `window.prompt/confirm/alert`.
|
|
35
|
+
- **Preview shells:** Plain / Desktop (mac window) / Mobile (phone), fullscreen.
|
|
36
|
+
- **Styling:** `.ce-*` class contract (300px rails, selection outline, drop
|
|
37
|
+
accents, pills/toggles, toolbar groups, badges, modals, preview shells),
|
|
38
|
+
token-driven light + neutral-black dark.
|
|
39
|
+
- `BrandInfo.org` (companyName/address/websiteUrl/privacyUrl/contactUrl/logoUrl)
|
|
40
|
+
for hosts to feed footer defaults.
|
|
41
|
+
|
|
3
42
|
## 0.2.0
|
|
4
43
|
|
|
5
44
|
- **Passthrough `unsupported` block** — a non-destructive block type so hosts
|
package/README.md
CHANGED
|
@@ -25,6 +25,14 @@ and a Vite (React 18) app alike.
|
|
|
25
25
|
| `@the-portland-company/canvas-editor/skeletons` | `Skeleton` / `SkeletonBar` (self-contained, token-driven) | Server-renderable. |
|
|
26
26
|
| `@the-portland-company/canvas-editor/css` | Primitive component classes (`.card`/`.input`/`.btn`/`.skeleton`…) | Import once in the app. |
|
|
27
27
|
|
|
28
|
+
### Peer dependencies (install alongside)
|
|
29
|
+
|
|
30
|
+
React 18 || 19, `@dnd-kit/{core,sortable,utilities}`, and — since 0.3.0, for the
|
|
31
|
+
tiptap rich-text editor — `@tiptap/core @tiptap/react @tiptap/starter-kit
|
|
32
|
+
@tiptap/extension-underline @tiptap/extension-text-align @tiptap/extension-link
|
|
33
|
+
@tiptap/extension-text-style @tiptap/extension-color @tiptap/extension-font-family`
|
|
34
|
+
(^3.27) and `emoji-picker-react` (^4.19). The package externalizes all of them.
|
|
35
|
+
|
|
28
36
|
**Never import `/editor` from a server module** — `@dnd-kit` evaluates
|
|
29
37
|
`React.createContext` at module load and will crash in an RSC / server-action
|
|
30
38
|
context. Server code that needs block logic imports the root (`.`) only.
|
|
@@ -77,9 +85,13 @@ context. Server code that needs block logic imports the root (`.`) only.
|
|
|
77
85
|
initialUpdatedAt={updatedAt}
|
|
78
86
|
nextAction={{ label: "Next: Sender →", href: senderHref }}
|
|
79
87
|
metaPanel={({ doc, setMeta }) => <MyEmailMetaPanel doc={doc} setMeta={setMeta} />}
|
|
88
|
+
insertMode="center" // or "side" — host owns the preference
|
|
80
89
|
/>
|
|
81
90
|
```
|
|
82
91
|
|
|
92
|
+
`brand()` may also return `org: { companyName, address, websiteUrl, privacyUrl,
|
|
93
|
+
contactUrl, logoUrl }` so the Footer block derives its defaults from the host.
|
|
94
|
+
|
|
83
95
|
## The boundary
|
|
84
96
|
|
|
85
97
|
- **The package owns:** the document model (`CanvasDocument` = `name` + `blocks`
|
package/dist/canvas.css
CHANGED
|
@@ -65,3 +65,489 @@
|
|
|
65
65
|
border: 8px solid var(--color-border); border-radius: 2rem;
|
|
66
66
|
background: var(--color-surface-2); padding: 1rem 0.75rem; min-height: 12rem;
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
/* ==========================================================================
|
|
70
|
+
Canvas Editor layout + interaction classes (`.ce-*`)
|
|
71
|
+
--------------------------------------------------------------------------
|
|
72
|
+
PUBLIC CONTRACT — these are the class names editor components consume.
|
|
73
|
+
Everything is token-driven (var(--color-*)); `.dark` overrides exist only
|
|
74
|
+
where a token cannot express the value (shadow alpha, device chrome).
|
|
75
|
+
|
|
76
|
+
.ce-layout 3-column editor grid: 300px rail / canvas / 300px rail
|
|
77
|
+
.ce-layout.is-left-collapsed left rail collapsed to a 44px strip
|
|
78
|
+
.ce-rail a side rail panel (surface, border, scrolls)
|
|
79
|
+
.ce-rail-header sticky rail heading row
|
|
80
|
+
.ce-canvas center scroll column, 24px padding
|
|
81
|
+
.ce-canvas-sheet the white/surface page the blocks render on
|
|
82
|
+
.ce-block one block wrapper (hover/selection host)
|
|
83
|
+
.ce-block.is-selected 2px accent outline
|
|
84
|
+
.ce-block-toolbar floating hover toolbar (drag + delete), top:-40px right:0
|
|
85
|
+
.ce-block-toolbar-btn a button inside that toolbar
|
|
86
|
+
.ce-dropzone collapsed 8px drop target between blocks
|
|
87
|
+
.ce-dropzone.is-hover expanded 32px, accent dashed + tint
|
|
88
|
+
.ce-dropzone.is-drag-active armed while a drag is in flight
|
|
89
|
+
.ce-dropzone-label the 11px uppercase "Insert" prompt inside a dropzone
|
|
90
|
+
.ce-file-overlay dashed-accent full-surface file-drop overlay
|
|
91
|
+
.ce-insert-pill the round "+" affordance that opens the insert menu
|
|
92
|
+
.ce-insert-menu floating insert-menu card (scrolls, deep shadow)
|
|
93
|
+
.ce-insert-item one row in the insert menu
|
|
94
|
+
.ce-ghost drag ghost / dnd-kit DragOverlay card
|
|
95
|
+
.ce-pill outline pill (padding presets, filters)
|
|
96
|
+
.ce-pill.is-active solid accent pill
|
|
97
|
+
.ce-toggle full-radius segmented control wrapper
|
|
98
|
+
.ce-toggle-btn a segment; .is-active marks the selected one
|
|
99
|
+
.ce-toolbar rich-text toolbar row (wraps, .25rem gap)
|
|
100
|
+
.ce-btngroup attached button group inside a toolbar
|
|
101
|
+
.ce-toolbar-btn a toolbar button; .is-active = accent-tinted mark
|
|
102
|
+
.ce-badge-safe email-safe font badge (green tint)
|
|
103
|
+
.ce-badge-unsafe non-email-safe font badge (amber tint)
|
|
104
|
+
.ce-modal-backdrop dimmed modal backdrop
|
|
105
|
+
.ce-modal modal card; .ce-modal-sm/-lg size variants
|
|
106
|
+
.ce-modal-header / .ce-modal-body / .ce-modal-footer
|
|
107
|
+
.ce-preview-shell neutral stage a preview is centred on
|
|
108
|
+
.ce-preview-desktop mac-window chrome around a desktop preview
|
|
109
|
+
.ce-preview-mobile phone body around a mobile preview
|
|
110
|
+
.ce-toast-host fixed toast stack (bottom-right)
|
|
111
|
+
.ce-toast one toast; .is-success/.is-error/.is-info
|
|
112
|
+
========================================================================== */
|
|
113
|
+
|
|
114
|
+
/* ---- layout -------------------------------------------------------------- */
|
|
115
|
+
|
|
116
|
+
.ce-layout {
|
|
117
|
+
display: grid;
|
|
118
|
+
grid-template-columns: 300px 1fr 300px;
|
|
119
|
+
gap: 1rem;
|
|
120
|
+
height: calc(100vh - 8rem);
|
|
121
|
+
min-height: 32rem;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.ce-layout.is-left-collapsed { grid-template-columns: 44px 1fr 300px; }
|
|
125
|
+
|
|
126
|
+
@media (max-width: 1100px) {
|
|
127
|
+
.ce-layout, .ce-layout.is-left-collapsed { grid-template-columns: 1fr; height: auto; }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.ce-rail {
|
|
131
|
+
background: var(--color-surface);
|
|
132
|
+
border: 1px solid var(--color-border);
|
|
133
|
+
border-radius: var(--radius, 0.375rem);
|
|
134
|
+
overflow: auto;
|
|
135
|
+
padding: 0.75rem;
|
|
136
|
+
min-height: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.ce-rail-header {
|
|
140
|
+
position: sticky; top: -0.75rem; z-index: 1;
|
|
141
|
+
margin: -0.75rem -0.75rem 0.5rem;
|
|
142
|
+
padding: 0.6rem 0.75rem;
|
|
143
|
+
background: var(--color-surface);
|
|
144
|
+
border-bottom: 1px solid var(--color-border);
|
|
145
|
+
display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
|
|
146
|
+
font-size: 0.78rem; font-weight: 600; color: var(--color-text-muted);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.ce-canvas {
|
|
150
|
+
flex: 1 1 auto;
|
|
151
|
+
overflow: auto;
|
|
152
|
+
padding: 1.5rem;
|
|
153
|
+
background: var(--color-bg, var(--color-surface-2));
|
|
154
|
+
border: 1px solid var(--color-border);
|
|
155
|
+
border-radius: var(--radius, 0.375rem);
|
|
156
|
+
min-height: 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.ce-canvas-sheet {
|
|
160
|
+
margin: 0 auto;
|
|
161
|
+
max-width: 640px;
|
|
162
|
+
background: var(--color-surface);
|
|
163
|
+
border-radius: var(--radius, 0.375rem);
|
|
164
|
+
box-shadow: 0 14px 40px rgba(15, 23, 42, 0.14);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.dark .ce-canvas-sheet { box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45); }
|
|
168
|
+
|
|
169
|
+
/* ---- blocks -------------------------------------------------------------- */
|
|
170
|
+
|
|
171
|
+
.ce-block {
|
|
172
|
+
position: relative;
|
|
173
|
+
outline: 2px solid transparent;
|
|
174
|
+
outline-offset: 2px;
|
|
175
|
+
border-radius: var(--radius, 0.375rem);
|
|
176
|
+
transition: outline-color 0.12s, background 0.12s;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.ce-block:hover { outline-color: color-mix(in srgb, var(--color-accent) 45%, transparent); }
|
|
180
|
+
|
|
181
|
+
.ce-block.is-selected { outline: 2px solid var(--color-accent); }
|
|
182
|
+
|
|
183
|
+
.ce-block-toolbar {
|
|
184
|
+
position: absolute; top: -40px; right: 0; z-index: 5;
|
|
185
|
+
display: inline-flex; align-items: center; gap: 0.125rem;
|
|
186
|
+
padding: 0.2rem;
|
|
187
|
+
background: var(--color-surface);
|
|
188
|
+
border: 1px solid var(--color-border);
|
|
189
|
+
border-radius: 11px;
|
|
190
|
+
box-shadow: var(--shadow-sm, 0 1px 2px rgba(15, 23, 42, 0.08));
|
|
191
|
+
opacity: 0;
|
|
192
|
+
pointer-events: none;
|
|
193
|
+
transition: opacity 0.12s;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.ce-block:hover > .ce-block-toolbar,
|
|
197
|
+
.ce-block:focus-within > .ce-block-toolbar,
|
|
198
|
+
.ce-block.is-selected > .ce-block-toolbar { opacity: 1; pointer-events: auto; }
|
|
199
|
+
|
|
200
|
+
.ce-block-toolbar-btn {
|
|
201
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
202
|
+
height: 1.5rem; min-width: 1.5rem; padding: 0 0.3rem;
|
|
203
|
+
border: 0; border-radius: 8px; background: transparent;
|
|
204
|
+
color: var(--color-text-muted); font-size: 0.8rem; line-height: 1; cursor: pointer;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.ce-block-toolbar-btn:hover { background: var(--color-surface-2); color: var(--color-text); }
|
|
208
|
+
.ce-block-toolbar-btn.is-danger:hover { background: color-mix(in srgb, var(--color-red) 14%, transparent); color: var(--color-red); }
|
|
209
|
+
.ce-block-toolbar-btn.is-grab { cursor: grab; }
|
|
210
|
+
.ce-block-toolbar-btn.is-grab:active { cursor: grabbing; }
|
|
211
|
+
|
|
212
|
+
/* ---- drop targets -------------------------------------------------------- */
|
|
213
|
+
|
|
214
|
+
.ce-dropzone {
|
|
215
|
+
height: 8px;
|
|
216
|
+
margin: 0.125rem 0;
|
|
217
|
+
border-radius: var(--radius, 0.375rem);
|
|
218
|
+
border: 1px dashed transparent;
|
|
219
|
+
display: flex; align-items: center; justify-content: center;
|
|
220
|
+
overflow: hidden;
|
|
221
|
+
transition: height 0.14s ease, background 0.14s, border-color 0.14s;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.ce-dropzone.is-drag-active {
|
|
225
|
+
height: 20px;
|
|
226
|
+
border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
|
|
227
|
+
background: color-mix(in srgb, var(--color-accent) 5%, transparent);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.ce-dropzone.is-hover {
|
|
231
|
+
height: 32px;
|
|
232
|
+
border-color: var(--color-accent);
|
|
233
|
+
background: color-mix(in srgb, var(--color-accent) 10%, transparent);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.ce-dropzone-label {
|
|
237
|
+
font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600;
|
|
238
|
+
color: var(--color-accent);
|
|
239
|
+
opacity: 0;
|
|
240
|
+
transition: opacity 0.12s;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.ce-dropzone.is-hover .ce-dropzone-label { opacity: 1; }
|
|
244
|
+
|
|
245
|
+
.ce-file-overlay {
|
|
246
|
+
position: absolute; inset: 0; z-index: 20;
|
|
247
|
+
display: flex; align-items: center; justify-content: center;
|
|
248
|
+
flex-direction: column; gap: 0.4rem;
|
|
249
|
+
border: 2px dashed var(--color-accent);
|
|
250
|
+
border-radius: var(--radius, 0.375rem);
|
|
251
|
+
background: color-mix(in srgb, var(--color-accent) 10%, var(--color-surface));
|
|
252
|
+
font-size: 0.875rem; font-weight: 600; color: var(--color-accent);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* ---- insert menu --------------------------------------------------------- */
|
|
256
|
+
|
|
257
|
+
.ce-insert-pill {
|
|
258
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem;
|
|
259
|
+
height: 1.6rem; padding: 0 0.7rem;
|
|
260
|
+
border: 1px solid var(--color-accent);
|
|
261
|
+
border-radius: 999px;
|
|
262
|
+
background: var(--color-surface);
|
|
263
|
+
color: var(--color-accent);
|
|
264
|
+
font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600;
|
|
265
|
+
cursor: pointer;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.ce-insert-pill:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }
|
|
269
|
+
|
|
270
|
+
.ce-insert-menu {
|
|
271
|
+
background: var(--color-surface);
|
|
272
|
+
border: 1px solid var(--color-border);
|
|
273
|
+
border-radius: 11px;
|
|
274
|
+
box-shadow: 0 18px 40px -12px rgba(15, 23, 42, 0.28);
|
|
275
|
+
max-height: 24rem;
|
|
276
|
+
overflow-y: auto;
|
|
277
|
+
padding: 0.3rem;
|
|
278
|
+
min-width: 14rem;
|
|
279
|
+
z-index: 30;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.dark .ce-insert-menu { box-shadow: 0 18px 40px -12px rgba(0, 0, 0, 0.6); }
|
|
283
|
+
|
|
284
|
+
.ce-insert-item {
|
|
285
|
+
display: flex; align-items: center; gap: 0.5rem; width: 100%;
|
|
286
|
+
padding: 0.4rem 0.55rem;
|
|
287
|
+
border: 0; border-radius: 0.375rem; background: transparent;
|
|
288
|
+
color: var(--color-text); font-size: 0.875rem; text-align: left; cursor: pointer;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.ce-insert-item:hover { background: var(--color-surface-2); }
|
|
292
|
+
.ce-insert-item[aria-selected="true"] { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
|
|
293
|
+
|
|
294
|
+
.ce-ghost {
|
|
295
|
+
background: var(--color-surface);
|
|
296
|
+
border: 1px solid var(--color-accent);
|
|
297
|
+
border-radius: var(--radius, 0.375rem);
|
|
298
|
+
box-shadow: 0 18px 40px -12px rgba(15, 23, 42, 0.28);
|
|
299
|
+
padding: 0.5rem 0.75rem;
|
|
300
|
+
font-size: 0.875rem;
|
|
301
|
+
color: var(--color-text);
|
|
302
|
+
opacity: 0.95;
|
|
303
|
+
cursor: grabbing;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.dark .ce-ghost { box-shadow: 0 18px 40px -12px rgba(0, 0, 0, 0.6); }
|
|
307
|
+
|
|
308
|
+
/* ---- pills + segmented toggles ------------------------------------------- */
|
|
309
|
+
|
|
310
|
+
.ce-pill {
|
|
311
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem;
|
|
312
|
+
height: 1.75rem; padding: 0 0.7rem;
|
|
313
|
+
border: 1px solid var(--color-border);
|
|
314
|
+
border-radius: 999px;
|
|
315
|
+
background: var(--color-surface);
|
|
316
|
+
color: var(--color-text);
|
|
317
|
+
font-size: 0.78rem; font-weight: 600; white-space: nowrap; cursor: pointer;
|
|
318
|
+
transition: background 0.12s, border-color 0.12s, color 0.12s;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.ce-pill:hover { background: var(--color-surface-2); }
|
|
322
|
+
|
|
323
|
+
.ce-pill.is-active {
|
|
324
|
+
background: var(--color-accent);
|
|
325
|
+
border-color: var(--color-accent);
|
|
326
|
+
color: #fff;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.ce-pill.is-active:hover { background: var(--color-accent-hover, var(--color-accent)); }
|
|
330
|
+
|
|
331
|
+
.ce-pill:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
332
|
+
|
|
333
|
+
.ce-toggle {
|
|
334
|
+
display: inline-flex; align-items: center;
|
|
335
|
+
padding: 2px;
|
|
336
|
+
border: 1px solid var(--color-border);
|
|
337
|
+
border-radius: 999px;
|
|
338
|
+
background: var(--color-surface-2);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.ce-toggle-btn {
|
|
342
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem;
|
|
343
|
+
height: 1.6rem; padding: 0 0.8rem;
|
|
344
|
+
border: 0; border-radius: 999px; background: transparent;
|
|
345
|
+
color: var(--color-text-muted);
|
|
346
|
+
font-size: 0.78rem; font-weight: 600; cursor: pointer;
|
|
347
|
+
transition: background 0.12s, color 0.12s;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.ce-toggle-btn:hover { color: var(--color-text); }
|
|
351
|
+
|
|
352
|
+
.ce-toggle-btn.is-active {
|
|
353
|
+
background: var(--color-surface);
|
|
354
|
+
color: var(--color-text);
|
|
355
|
+
box-shadow: var(--shadow-sm, 0 1px 2px rgba(15, 23, 42, 0.08));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* ---- rich-text toolbar --------------------------------------------------- */
|
|
359
|
+
|
|
360
|
+
.ce-toolbar {
|
|
361
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem;
|
|
362
|
+
padding: 0.35rem;
|
|
363
|
+
background: var(--color-surface);
|
|
364
|
+
border: 1px solid var(--color-border);
|
|
365
|
+
border-radius: var(--radius, 0.375rem);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.ce-btngroup { display: inline-flex; align-items: center; }
|
|
369
|
+
|
|
370
|
+
.ce-btngroup > .ce-toolbar-btn { border-radius: 0; margin-left: -1px; }
|
|
371
|
+
.ce-btngroup > .ce-toolbar-btn:first-child { border-top-left-radius: 0.375rem; border-bottom-left-radius: 0.375rem; margin-left: 0; }
|
|
372
|
+
.ce-btngroup > .ce-toolbar-btn:last-child { border-top-right-radius: 0.375rem; border-bottom-right-radius: 0.375rem; }
|
|
373
|
+
|
|
374
|
+
.ce-toolbar-btn {
|
|
375
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 0.25rem;
|
|
376
|
+
height: 1.85rem; min-width: 1.85rem; padding: 0 0.45rem;
|
|
377
|
+
border: 1px solid var(--color-border);
|
|
378
|
+
border-radius: 0.375rem;
|
|
379
|
+
background: var(--color-surface);
|
|
380
|
+
color: var(--color-text);
|
|
381
|
+
font-size: 0.78rem; font-weight: 600; line-height: 1; cursor: pointer;
|
|
382
|
+
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.ce-toolbar-btn:hover { background: var(--color-surface-2); }
|
|
386
|
+
|
|
387
|
+
.ce-toolbar-btn.is-active {
|
|
388
|
+
background: color-mix(in srgb, var(--color-accent) 14%, transparent);
|
|
389
|
+
border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
|
|
390
|
+
color: var(--color-accent);
|
|
391
|
+
position: relative; z-index: 1;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.ce-toolbar-btn:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
395
|
+
|
|
396
|
+
.ce-toolbar-sep { width: 1px; align-self: stretch; margin: 0.15rem 0.2rem; background: var(--color-border); }
|
|
397
|
+
|
|
398
|
+
/* ---- email-safe font badges ---------------------------------------------- */
|
|
399
|
+
|
|
400
|
+
.ce-badge-safe, .ce-badge-unsafe {
|
|
401
|
+
display: inline-flex; align-items: center; gap: 0.25rem;
|
|
402
|
+
padding: 0.1rem 0.45rem;
|
|
403
|
+
border-radius: 999px;
|
|
404
|
+
border: 1px solid transparent;
|
|
405
|
+
font-size: 0.7rem; font-weight: 600; white-space: nowrap;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.ce-badge-safe {
|
|
409
|
+
background: color-mix(in srgb, var(--color-green, #10b981) 14%, transparent);
|
|
410
|
+
border-color: color-mix(in srgb, var(--color-green, #10b981) 40%, transparent);
|
|
411
|
+
color: var(--color-green, #10b981);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.ce-badge-unsafe {
|
|
415
|
+
background: color-mix(in srgb, var(--color-yellow, #f59e0b) 16%, transparent);
|
|
416
|
+
border-color: color-mix(in srgb, var(--color-yellow, #f59e0b) 42%, transparent);
|
|
417
|
+
color: var(--color-yellow, #f59e0b);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* ---- modals -------------------------------------------------------------- */
|
|
421
|
+
|
|
422
|
+
.ce-modal-backdrop {
|
|
423
|
+
position: fixed; inset: 0; z-index: 50;
|
|
424
|
+
display: flex; align-items: center; justify-content: center;
|
|
425
|
+
padding: 1rem;
|
|
426
|
+
background: rgba(15, 23, 42, 0.38);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.dark .ce-modal-backdrop { background: rgba(0, 0, 0, 0.6); }
|
|
430
|
+
|
|
431
|
+
.ce-modal {
|
|
432
|
+
display: flex; flex-direction: column;
|
|
433
|
+
width: 100%; max-width: 32rem; max-height: 85vh;
|
|
434
|
+
background: var(--color-surface);
|
|
435
|
+
border: 1px solid var(--color-border);
|
|
436
|
+
border-radius: 11px;
|
|
437
|
+
box-shadow: 0 18px 40px -12px rgba(15, 23, 42, 0.28);
|
|
438
|
+
overflow: hidden;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.dark .ce-modal { box-shadow: 0 18px 40px -12px rgba(0, 0, 0, 0.7); }
|
|
442
|
+
|
|
443
|
+
.ce-modal-sm { max-width: 24rem; }
|
|
444
|
+
.ce-modal-lg { max-width: 48rem; }
|
|
445
|
+
|
|
446
|
+
.ce-modal-header {
|
|
447
|
+
display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
|
|
448
|
+
padding: 0.75rem 1rem;
|
|
449
|
+
border-bottom: 1px solid var(--color-border);
|
|
450
|
+
font-size: 0.875rem; font-weight: 600; color: var(--color-text);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.ce-modal-body { padding: 1rem; overflow-y: auto; font-size: 0.875rem; color: var(--color-text); }
|
|
454
|
+
|
|
455
|
+
.ce-modal-footer {
|
|
456
|
+
display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem;
|
|
457
|
+
padding: 0.75rem 1rem;
|
|
458
|
+
border-top: 1px solid var(--color-border);
|
|
459
|
+
background: var(--color-surface-2);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* ---- previews ------------------------------------------------------------ */
|
|
463
|
+
|
|
464
|
+
.ce-preview-shell {
|
|
465
|
+
display: flex; align-items: flex-start; justify-content: center;
|
|
466
|
+
padding: 1.5rem;
|
|
467
|
+
background: var(--color-surface-2);
|
|
468
|
+
border-radius: 18px;
|
|
469
|
+
overflow: auto;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.ce-preview-desktop {
|
|
473
|
+
width: 100%; max-width: 880px;
|
|
474
|
+
background: var(--color-surface);
|
|
475
|
+
border: 1px solid var(--color-border);
|
|
476
|
+
border-radius: 18px;
|
|
477
|
+
box-shadow: 0 14px 40px rgba(15, 23, 42, 0.14);
|
|
478
|
+
overflow: hidden;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.dark .ce-preview-desktop { box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45); }
|
|
482
|
+
|
|
483
|
+
.ce-preview-desktop::before {
|
|
484
|
+
content: "";
|
|
485
|
+
display: block; height: 34px;
|
|
486
|
+
background: var(--color-surface-2);
|
|
487
|
+
border-bottom: 1px solid var(--color-border);
|
|
488
|
+
/* three mac window dots — device chrome, intentionally literal colors */
|
|
489
|
+
background-image:
|
|
490
|
+
radial-gradient(circle 5px at 18px 17px, #ef4444 98%, transparent 100%),
|
|
491
|
+
radial-gradient(circle 5px at 36px 17px, #f59e0b 98%, transparent 100%),
|
|
492
|
+
radial-gradient(circle 5px at 54px 17px, #10b981 98%, transparent 100%);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.ce-preview-mobile {
|
|
496
|
+
width: 100%; max-width: 430px;
|
|
497
|
+
padding: 14px;
|
|
498
|
+
/* phone body: a device color, not a theme surface */
|
|
499
|
+
background: #202124;
|
|
500
|
+
border-radius: 32px;
|
|
501
|
+
box-shadow: 0 14px 40px rgba(15, 23, 42, 0.14);
|
|
502
|
+
position: relative;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.dark .ce-preview-mobile { box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45); }
|
|
506
|
+
|
|
507
|
+
.ce-preview-mobile::before {
|
|
508
|
+
content: "";
|
|
509
|
+
position: absolute; top: 6px; left: 50%; transform: translateX(-50%);
|
|
510
|
+
width: 96px; height: 5px; border-radius: 999px;
|
|
511
|
+
background: #3a3a3a;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.ce-preview-mobile > * {
|
|
515
|
+
display: block;
|
|
516
|
+
background: var(--color-surface);
|
|
517
|
+
border-radius: 20px;
|
|
518
|
+
overflow: hidden;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* ---- toasts -------------------------------------------------------------- */
|
|
522
|
+
|
|
523
|
+
.ce-toast-host {
|
|
524
|
+
position: fixed; right: 1rem; bottom: 1rem; z-index: 60;
|
|
525
|
+
display: flex; flex-direction: column; gap: 0.5rem;
|
|
526
|
+
pointer-events: none;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.ce-toast {
|
|
530
|
+
pointer-events: auto;
|
|
531
|
+
display: flex; align-items: center; gap: 0.5rem;
|
|
532
|
+
min-width: 14rem; max-width: 22rem;
|
|
533
|
+
padding: 0.6rem 0.8rem;
|
|
534
|
+
background: var(--color-surface);
|
|
535
|
+
border: 1px solid var(--color-border);
|
|
536
|
+
border-left: 3px solid var(--color-border);
|
|
537
|
+
border-radius: 11px;
|
|
538
|
+
box-shadow: 0 18px 40px -12px rgba(15, 23, 42, 0.28);
|
|
539
|
+
font-size: 0.875rem; color: var(--color-text);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.dark .ce-toast { box-shadow: 0 18px 40px -12px rgba(0, 0, 0, 0.6); }
|
|
543
|
+
|
|
544
|
+
.ce-toast.is-success { border-left-color: var(--color-green, #10b981); }
|
|
545
|
+
.ce-toast.is-error { border-left-color: var(--color-red); }
|
|
546
|
+
.ce-toast.is-info { border-left-color: var(--color-accent); }
|
|
547
|
+
|
|
548
|
+
.ce-toast-close {
|
|
549
|
+
margin-left: auto; border: 0; background: transparent; cursor: pointer;
|
|
550
|
+
color: var(--color-text-muted); font-size: 0.85rem; line-height: 1; padding: 0.1rem 0.2rem;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.ce-toast-close:hover { color: var(--color-text); }
|