@caretcms/core 0.1.0 → 0.1.2
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/README.md +182 -6
- package/dist/browser-runtime.d.ts +16 -0
- package/dist/browser-runtime.d.ts.map +1 -1
- package/dist/browser-runtime.js +42 -23
- package/dist/browser-runtime.js.map +1 -1
- package/dist/contracts.d.ts +14 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +14 -0
- package/dist/contracts.js.map +1 -0
- package/dist/editable.d.ts +28 -0
- package/dist/editable.d.ts.map +1 -0
- package/dist/editable.js +122 -0
- package/dist/editable.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +274 -4
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +45 -3
- package/dist/loader.js.map +1 -1
- package/dist/providers/storage/markdown.d.ts +7 -0
- package/dist/providers/storage/markdown.d.ts.map +1 -0
- package/dist/providers/storage/markdown.js +6 -0
- package/dist/providers/storage/markdown.js.map +1 -0
- package/dist/runtime/auth/cookie-utils.d.ts +1 -0
- package/dist/runtime/auth/cookie-utils.d.ts.map +1 -1
- package/dist/runtime/auth/cookie-utils.js +1 -1
- package/dist/runtime/auth/cookie-utils.js.map +1 -1
- package/dist/runtime/auth/rate-limiter.d.ts.map +1 -1
- package/dist/runtime/auth/rate-limiter.js +19 -13
- package/dist/runtime/auth/rate-limiter.js.map +1 -1
- package/dist/runtime/auth/session.d.ts +8 -0
- package/dist/runtime/auth/session.d.ts.map +1 -1
- package/dist/runtime/auth/session.js +72 -11
- package/dist/runtime/auth/session.js.map +1 -1
- package/dist/runtime/config.d.ts +1 -0
- package/dist/runtime/config.d.ts.map +1 -1
- package/dist/runtime/config.js +2 -1
- package/dist/runtime/config.js.map +1 -1
- package/dist/runtime/git-journal.d.ts +19 -0
- package/dist/runtime/git-journal.d.ts.map +1 -0
- package/dist/runtime/git-journal.js +64 -0
- package/dist/runtime/git-journal.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/middleware.d.ts.map +1 -1
- package/dist/runtime/middleware.js +125 -8
- package/dist/runtime/middleware.js.map +1 -1
- package/dist/runtime/mutations/contracts.d.ts.map +1 -1
- package/dist/runtime/mutations/contracts.js +7 -4
- package/dist/runtime/mutations/contracts.js.map +1 -1
- package/dist/runtime/mutations/engine.d.ts +7 -0
- package/dist/runtime/mutations/engine.d.ts.map +1 -1
- package/dist/runtime/mutations/engine.js +30 -17
- package/dist/runtime/mutations/engine.js.map +1 -1
- package/dist/runtime/providers.d.ts +16 -0
- package/dist/runtime/providers.d.ts.map +1 -1
- package/dist/runtime/providers.js +16 -0
- package/dist/runtime/providers.js.map +1 -1
- package/dist/runtime/publish.d.ts +40 -0
- package/dist/runtime/publish.d.ts.map +1 -0
- package/dist/runtime/publish.js +75 -0
- package/dist/runtime/publish.js.map +1 -0
- package/dist/runtime/rebuild-webhook.d.ts +29 -0
- package/dist/runtime/rebuild-webhook.d.ts.map +1 -0
- package/dist/runtime/rebuild-webhook.js +37 -0
- package/dist/runtime/rebuild-webhook.js.map +1 -0
- package/dist/runtime/request-context.d.ts +19 -0
- package/dist/runtime/request-context.d.ts.map +1 -1
- package/dist/runtime/request-context.js.map +1 -1
- package/dist/runtime/rewrite.d.ts +29 -1
- package/dist/runtime/rewrite.d.ts.map +1 -1
- package/dist/runtime/rewrite.js +30 -16
- package/dist/runtime/rewrite.js.map +1 -1
- package/dist/runtime/rich-allowlist.d.ts +29 -0
- package/dist/runtime/rich-allowlist.d.ts.map +1 -0
- package/dist/runtime/rich-allowlist.js +49 -0
- package/dist/runtime/rich-allowlist.js.map +1 -0
- package/dist/runtime/routes/_helpers.d.ts +33 -0
- package/dist/runtime/routes/_helpers.d.ts.map +1 -1
- package/dist/runtime/routes/_helpers.js +117 -1
- package/dist/runtime/routes/_helpers.js.map +1 -1
- package/dist/runtime/routes/admin-entry.d.ts +1 -0
- package/dist/runtime/routes/admin-entry.d.ts.map +1 -1
- package/dist/runtime/routes/admin-entry.js +3 -2
- package/dist/runtime/routes/admin-entry.js.map +1 -1
- package/dist/runtime/routes/admin.d.ts +1 -0
- package/dist/runtime/routes/admin.d.ts.map +1 -1
- package/dist/runtime/routes/admin.js +61 -9
- package/dist/runtime/routes/admin.js.map +1 -1
- package/dist/runtime/routes/auth-login.d.ts +1 -0
- package/dist/runtime/routes/auth-login.d.ts.map +1 -1
- package/dist/runtime/routes/auth-login.js +26 -8
- package/dist/runtime/routes/auth-login.js.map +1 -1
- package/dist/runtime/routes/auth-logout.d.ts +1 -0
- package/dist/runtime/routes/auth-logout.d.ts.map +1 -1
- package/dist/runtime/routes/auth-logout.js +5 -0
- package/dist/runtime/routes/auth-logout.js.map +1 -1
- package/dist/runtime/routes/auth-session.d.ts +1 -0
- package/dist/runtime/routes/auth-session.d.ts.map +1 -1
- package/dist/runtime/routes/auth-session.js +1 -0
- package/dist/runtime/routes/auth-session.js.map +1 -1
- package/dist/runtime/routes/collection-list.d.ts +1 -0
- package/dist/runtime/routes/collection-list.d.ts.map +1 -1
- package/dist/runtime/routes/collection-list.js +3 -2
- package/dist/runtime/routes/collection-list.js.map +1 -1
- package/dist/runtime/routes/collections-metadata.d.ts +1 -0
- package/dist/runtime/routes/collections-metadata.d.ts.map +1 -1
- package/dist/runtime/routes/collections-metadata.js +7 -4
- package/dist/runtime/routes/collections-metadata.js.map +1 -1
- package/dist/runtime/routes/draft.d.ts +13 -0
- package/dist/runtime/routes/draft.d.ts.map +1 -0
- package/dist/runtime/routes/draft.js +55 -0
- package/dist/runtime/routes/draft.js.map +1 -0
- package/dist/runtime/routes/editor-assets.d.ts +1 -0
- package/dist/runtime/routes/editor-assets.d.ts.map +1 -1
- package/dist/runtime/routes/editor-assets.js +1 -0
- package/dist/runtime/routes/editor-assets.js.map +1 -1
- package/dist/runtime/routes/entries.d.ts +1 -0
- package/dist/runtime/routes/entries.d.ts.map +1 -1
- package/dist/runtime/routes/entries.js +3 -2
- package/dist/runtime/routes/entries.js.map +1 -1
- package/dist/runtime/routes/history.d.ts +1 -0
- package/dist/runtime/routes/history.d.ts.map +1 -1
- package/dist/runtime/routes/history.js +24 -10
- package/dist/runtime/routes/history.js.map +1 -1
- package/dist/runtime/routes/mutate.d.ts +1 -0
- package/dist/runtime/routes/mutate.d.ts.map +1 -1
- package/dist/runtime/routes/mutate.js +7 -10
- package/dist/runtime/routes/mutate.js.map +1 -1
- package/dist/runtime/routes/publish.d.ts +10 -0
- package/dist/runtime/routes/publish.d.ts.map +1 -0
- package/dist/runtime/routes/publish.js +75 -0
- package/dist/runtime/routes/publish.js.map +1 -0
- package/dist/runtime/routes/schema.d.ts +1 -0
- package/dist/runtime/routes/schema.d.ts.map +1 -1
- package/dist/runtime/routes/schema.js +7 -4
- package/dist/runtime/routes/schema.js.map +1 -1
- package/dist/runtime/routes/studio-home.d.ts +1 -0
- package/dist/runtime/routes/studio-home.d.ts.map +1 -1
- package/dist/runtime/routes/studio-home.js +10 -9
- package/dist/runtime/routes/studio-home.js.map +1 -1
- package/dist/runtime/routes/theme-stylesheet.d.ts +1 -0
- package/dist/runtime/routes/theme-stylesheet.d.ts.map +1 -1
- package/dist/runtime/routes/theme-stylesheet.js +1 -0
- package/dist/runtime/routes/theme-stylesheet.js.map +1 -1
- package/dist/runtime/routes/upload.d.ts +1 -0
- package/dist/runtime/routes/upload.d.ts.map +1 -1
- package/dist/runtime/routes/upload.js +17 -3
- package/dist/runtime/routes/upload.js.map +1 -1
- package/dist/runtime/sanitize-html.d.ts +8 -2
- package/dist/runtime/sanitize-html.d.ts.map +1 -1
- package/dist/runtime/sanitize-html.js +57 -38
- package/dist/runtime/sanitize-html.js.map +1 -1
- package/dist/runtime/static-bake.d.ts +20 -0
- package/dist/runtime/static-bake.d.ts.map +1 -0
- package/dist/runtime/static-bake.js +58 -0
- package/dist/runtime/static-bake.js.map +1 -0
- package/dist/runtime/stega.d.ts +38 -0
- package/dist/runtime/stega.d.ts.map +1 -0
- package/dist/runtime/stega.js +98 -0
- package/dist/runtime/stega.js.map +1 -0
- package/dist/runtime/storage/atomic-write.d.ts +22 -0
- package/dist/runtime/storage/atomic-write.d.ts.map +1 -0
- package/dist/runtime/storage/atomic-write.js +44 -0
- package/dist/runtime/storage/atomic-write.js.map +1 -0
- package/dist/runtime/storage/filesystem-adapter.d.ts +14 -11
- package/dist/runtime/storage/filesystem-adapter.d.ts.map +1 -1
- package/dist/runtime/storage/filesystem-adapter.js +71 -201
- package/dist/runtime/storage/filesystem-adapter.js.map +1 -1
- package/dist/runtime/storage/frontmatter-codec.d.ts +47 -0
- package/dist/runtime/storage/frontmatter-codec.d.ts.map +1 -0
- package/dist/runtime/storage/frontmatter-codec.js +523 -0
- package/dist/runtime/storage/frontmatter-codec.js.map +1 -0
- package/dist/runtime/storage/fs-runtime.d.ts +8 -0
- package/dist/runtime/storage/fs-runtime.d.ts.map +1 -0
- package/dist/runtime/storage/fs-runtime.js +40 -0
- package/dist/runtime/storage/fs-runtime.js.map +1 -0
- package/dist/runtime/storage/id-contracts.d.ts +22 -0
- package/dist/runtime/storage/id-contracts.d.ts.map +1 -0
- package/dist/runtime/storage/id-contracts.js +27 -0
- package/dist/runtime/storage/id-contracts.js.map +1 -0
- package/dist/runtime/storage/image-validation.d.ts +10 -0
- package/dist/runtime/storage/image-validation.d.ts.map +1 -1
- package/dist/runtime/storage/image-validation.js +17 -4
- package/dist/runtime/storage/image-validation.js.map +1 -1
- package/dist/runtime/storage/in-memory-adapter.d.ts +4 -0
- package/dist/runtime/storage/in-memory-adapter.d.ts.map +1 -1
- package/dist/runtime/storage/in-memory-adapter.js +11 -0
- package/dist/runtime/storage/in-memory-adapter.js.map +1 -1
- package/dist/runtime/storage/markdown-adapter.d.ts +63 -0
- package/dist/runtime/storage/markdown-adapter.d.ts.map +1 -0
- package/dist/runtime/storage/markdown-adapter.js +227 -0
- package/dist/runtime/storage/markdown-adapter.js.map +1 -0
- package/dist/runtime/storage/quota-upload-handler.d.ts +2 -0
- package/dist/runtime/storage/quota-upload-handler.d.ts.map +1 -1
- package/dist/runtime/storage/quota-upload-handler.js +34 -8
- package/dist/runtime/storage/quota-upload-handler.js.map +1 -1
- package/dist/runtime/storage/session-overlay-adapter.d.ts +3 -0
- package/dist/runtime/storage/session-overlay-adapter.d.ts.map +1 -1
- package/dist/runtime/storage/session-overlay-adapter.js +3 -1
- package/dist/runtime/storage/session-overlay-adapter.js.map +1 -1
- package/dist/runtime/storage/sidecar-meta-store.d.ts +51 -0
- package/dist/runtime/storage/sidecar-meta-store.d.ts.map +1 -0
- package/dist/runtime/storage/sidecar-meta-store.js +209 -0
- package/dist/runtime/storage/sidecar-meta-store.js.map +1 -0
- package/dist/runtime/themes/preset-tokens.d.ts +0 -1
- package/dist/runtime/themes/preset-tokens.d.ts.map +1 -1
- package/dist/runtime/themes/preset-tokens.js +1 -1
- package/dist/runtime/themes/preset-tokens.js.map +1 -1
- package/dist/runtime/utils.d.ts +9 -1
- package/dist/runtime/utils.d.ts.map +1 -1
- package/dist/runtime/utils.js +21 -1
- package/dist/runtime/utils.js.map +1 -1
- package/dist/runtime/views/studio-layout.d.ts.map +1 -1
- package/dist/runtime/views/studio-layout.js +11 -10
- package/dist/runtime/views/studio-layout.js.map +1 -1
- package/dist/schema-utils.d.ts.map +1 -1
- package/dist/schema-utils.js +1 -0
- package/dist/schema-utils.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -1
- package/static/cms/dev-toolbar/app.js +265 -0
- package/static/cms/editor/config.js +10 -0
- package/static/cms/editor/helpers.js +5 -1
- package/static/cms/editor/highlight.js +44 -0
- package/static/cms/editor/sanitize.js +56 -9
- package/static/cms/editor/stega-hydrate.js +80 -0
- package/static/cms/editor/text-edit.js +22 -0
- package/static/cms/editor/tokens-and-text.css +1 -1
- package/static/cms/editor/toolbar.css +101 -4
- package/static/cms/editor/toolbar.js +171 -21
- package/static/cms/editor.js +18 -1
- package/static/cms/signin-hint.css +74 -0
- package/static/cms/studio-login.css +70 -0
- package/static/cms/studio.css +80 -13
- package/dist/runtime/schema/template.d.ts +0 -15
- package/dist/runtime/schema/template.d.ts.map +0 -1
- package/dist/runtime/schema/template.js +0 -38
- package/dist/runtime/schema/template.js.map +0 -1
|
@@ -78,6 +78,45 @@
|
|
|
78
78
|
display: flex;
|
|
79
79
|
align-items: center;
|
|
80
80
|
gap: 12px;
|
|
81
|
+
min-width: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@media (max-width: 768px) {
|
|
85
|
+
.cms-toolbar-inner {
|
|
86
|
+
padding: 0 12px;
|
|
87
|
+
gap: 8px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.cms-toolbar-left {
|
|
91
|
+
gap: 8px;
|
|
92
|
+
flex-shrink: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.cms-status-text {
|
|
96
|
+
display: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.cms-toolbar-right {
|
|
100
|
+
gap: 6px;
|
|
101
|
+
overflow-x: auto;
|
|
102
|
+
-webkit-overflow-scrolling: touch;
|
|
103
|
+
scrollbar-width: none;
|
|
104
|
+
flex-shrink: 1;
|
|
105
|
+
padding-left: 4px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.cms-toolbar-right::-webkit-scrollbar {
|
|
109
|
+
display: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.cms-toolbar-divider {
|
|
113
|
+
display: none;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.cms-toolbar button {
|
|
117
|
+
padding: 6px 10px;
|
|
118
|
+
flex-shrink: 0;
|
|
119
|
+
}
|
|
81
120
|
}
|
|
82
121
|
|
|
83
122
|
/* Shared base for every toolbar button so they can't drift visually. Individual
|
|
@@ -88,10 +127,10 @@
|
|
|
88
127
|
gap: 6px;
|
|
89
128
|
padding: 6px 14px;
|
|
90
129
|
font-family: var(--cms-font);
|
|
91
|
-
font-size:
|
|
130
|
+
font-size: 11px;
|
|
92
131
|
font-weight: 500;
|
|
93
132
|
line-height: 1.2;
|
|
94
|
-
letter-spacing: 0.
|
|
133
|
+
letter-spacing: 0.08em;
|
|
95
134
|
text-transform: uppercase;
|
|
96
135
|
background: transparent;
|
|
97
136
|
color: rgba(255, 255, 255, 0.5);
|
|
@@ -175,6 +214,52 @@
|
|
|
175
214
|
color: rgba(59, 130, 246, 0.9);
|
|
176
215
|
}
|
|
177
216
|
|
|
217
|
+
/* The base `.cms-toolbar button` sets `display`, which overrides the default
|
|
218
|
+
[hidden] behavior — restore it so hidden draft actions actually disappear. */
|
|
219
|
+
.cms-toolbar button[hidden] {
|
|
220
|
+
display: none;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Draft preview active — amber, signalling "unpublished" */
|
|
224
|
+
.cms-toolbar button.cms-preview-btn-active {
|
|
225
|
+
background: rgba(234, 179, 8, 0.12);
|
|
226
|
+
border-color: rgba(234, 179, 8, 0.4);
|
|
227
|
+
color: var(--cms-yellow);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Go live — primary action on static delivery */
|
|
231
|
+
.cms-toolbar button.cms-go-live-btn {
|
|
232
|
+
background: rgba(34, 197, 94, 0.12);
|
|
233
|
+
border-color: rgba(34, 197, 94, 0.3);
|
|
234
|
+
color: var(--cms-green);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.cms-toolbar button.cms-go-live-btn:hover {
|
|
238
|
+
background: rgba(34, 197, 94, 0.25);
|
|
239
|
+
border-color: rgba(34, 197, 94, 0.5);
|
|
240
|
+
color: var(--cms-green);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* Publish — green (server delivery when preview is manually enabled) */
|
|
244
|
+
.cms-toolbar button.cms-publish-btn {
|
|
245
|
+
background: rgba(34, 197, 94, 0.12);
|
|
246
|
+
border-color: rgba(34, 197, 94, 0.3);
|
|
247
|
+
color: var(--cms-green);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.cms-toolbar button.cms-publish-btn:hover {
|
|
251
|
+
background: rgba(34, 197, 94, 0.25);
|
|
252
|
+
border-color: rgba(34, 197, 94, 0.5);
|
|
253
|
+
color: var(--cms-green);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Discard — red on hover */
|
|
257
|
+
.cms-toolbar button.cms-discard-btn:hover {
|
|
258
|
+
background: rgba(248, 113, 113, 0.08);
|
|
259
|
+
border-color: rgba(248, 113, 113, 0.3);
|
|
260
|
+
color: var(--cms-red);
|
|
261
|
+
}
|
|
262
|
+
|
|
178
263
|
/* Highlight all mode — show every editable region */
|
|
179
264
|
.cms-highlight-all [data-caret].cms-editable {
|
|
180
265
|
outline-color: var(--cms-blue-hover);
|
|
@@ -186,6 +271,15 @@
|
|
|
186
271
|
outline-offset: 4px;
|
|
187
272
|
}
|
|
188
273
|
|
|
274
|
+
/* The welcome reveal still flashes editable regions, but without animating the
|
|
275
|
+
transition — motion is suppressed, the information is not. */
|
|
276
|
+
@media (prefers-reduced-motion: reduce) {
|
|
277
|
+
.cms-highlight-all [data-caret].cms-editable,
|
|
278
|
+
.cms-highlight-all img[data-caret] {
|
|
279
|
+
transition: none;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
189
283
|
/* ---------------------------------------------------------------------------
|
|
190
284
|
Toolbar — page navigation links
|
|
191
285
|
--------------------------------------------------------------------------- */
|
|
@@ -204,8 +298,11 @@
|
|
|
204
298
|
|
|
205
299
|
.cms-nav-link {
|
|
206
300
|
padding: 5px 14px;
|
|
207
|
-
font-
|
|
208
|
-
|
|
301
|
+
font-family: var(--cms-font);
|
|
302
|
+
font-size: 11px;
|
|
303
|
+
font-weight: 500;
|
|
304
|
+
line-height: 1.2;
|
|
305
|
+
letter-spacing: 0.08em;
|
|
209
306
|
text-transform: uppercase;
|
|
210
307
|
text-decoration: none;
|
|
211
308
|
color: rgba(255, 255, 255, 0.35);
|
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
import { toggleHighlight } from './highlight.js';
|
|
2
|
+
import { buildCmsUrl, isStaticDelivery } from './config.js';
|
|
3
|
+
|
|
4
|
+
const PREVIEW_COOKIE = 'caret_preview';
|
|
5
|
+
|
|
6
|
+
function previewActive() {
|
|
7
|
+
return document.cookie.split('; ').some((c) => c === `${PREVIEW_COOKIE}=1`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function setPreviewCookie(on) {
|
|
11
|
+
document.cookie = on
|
|
12
|
+
? `${PREVIEW_COOKIE}=1; path=/; SameSite=Lax`
|
|
13
|
+
: `${PREVIEW_COOKIE}=; path=/; Max-Age=0; SameSite=Lax`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Static delivery always drafts — turn preview on once so saves stay unpublished. */
|
|
17
|
+
export function ensureStaticPreviewMode() {
|
|
18
|
+
if (!isStaticDelivery()) return false;
|
|
19
|
+
if (previewActive()) return false;
|
|
20
|
+
setPreviewCookie(true);
|
|
21
|
+
window.location.reload();
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Align preview cookie with delivery mode (static on, server off). */
|
|
26
|
+
export function normalizePreviewForDelivery() {
|
|
27
|
+
if (isStaticDelivery()) return ensureStaticPreviewMode();
|
|
28
|
+
if (previewActive()) {
|
|
29
|
+
setPreviewCookie(false);
|
|
30
|
+
window.location.reload();
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
1
36
|
function getToolbarNavLinks(pagePath) {
|
|
2
37
|
const links = [];
|
|
3
38
|
const headerNav = document.querySelector('#main-header nav');
|
|
@@ -14,7 +49,22 @@ function getToolbarNavLinks(pagePath) {
|
|
|
14
49
|
return links;
|
|
15
50
|
}
|
|
16
51
|
|
|
17
|
-
function
|
|
52
|
+
function renderDraftControls(staticDelivery) {
|
|
53
|
+
if (!staticDelivery) return '';
|
|
54
|
+
|
|
55
|
+
return `
|
|
56
|
+
<button class="cms-publish-btn cms-go-live-btn" type="button" title="Push your draft changes to the live site">
|
|
57
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5"/><polyline points="5 12 12 5 19 12"/></svg>
|
|
58
|
+
<span class="cms-go-live-label">Go live</span>
|
|
59
|
+
</button>
|
|
60
|
+
<button class="cms-discard-btn" type="button" title="Discard your draft changes">
|
|
61
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
|
62
|
+
Discard
|
|
63
|
+
</button>
|
|
64
|
+
<div class="cms-toolbar-divider"></div>`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderToolbar(navLinks, staticDelivery) {
|
|
18
68
|
const navLinksHtml = navLinks
|
|
19
69
|
.map(
|
|
20
70
|
({ href, label, isActive }) =>
|
|
@@ -22,14 +72,20 @@ function renderToolbar(navLinks) {
|
|
|
22
72
|
)
|
|
23
73
|
.join('');
|
|
24
74
|
|
|
75
|
+
const badgeLabel = staticDelivery ? 'Draft' : 'Live';
|
|
76
|
+
const badgeHint = staticDelivery
|
|
77
|
+
? 'Not on your site until you go live'
|
|
78
|
+
: 'Changes save directly to your site';
|
|
79
|
+
|
|
25
80
|
const toolbar = document.createElement('div');
|
|
26
81
|
toolbar.className = 'cms-toolbar';
|
|
82
|
+
toolbar.dataset.delivery = staticDelivery ? 'static' : 'server';
|
|
27
83
|
toolbar.innerHTML = `
|
|
28
84
|
<div class="cms-toolbar-inner">
|
|
29
85
|
<div class="cms-toolbar-left">
|
|
30
|
-
<div class="cms-toolbar-badge">
|
|
86
|
+
<div class="cms-toolbar-badge" title="${badgeHint}">
|
|
31
87
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L2 9l10 13 10-13L12 2zm0 3.84L18.26 9 12 17.65 5.74 9 12 5.84z"/></svg>
|
|
32
|
-
<span class="cms-toolbar-badge-text"
|
|
88
|
+
<span class="cms-toolbar-badge-text">${badgeLabel}</span>
|
|
33
89
|
</div>
|
|
34
90
|
<div class="cms-toolbar-divider"></div>
|
|
35
91
|
<div class="cms-status-group">
|
|
@@ -39,6 +95,7 @@ function renderToolbar(navLinks) {
|
|
|
39
95
|
</div>
|
|
40
96
|
${navLinksHtml ? `<div class="cms-toolbar-nav">${navLinksHtml}</div>` : ''}
|
|
41
97
|
<div class="cms-toolbar-right">
|
|
98
|
+
${renderDraftControls(staticDelivery)}
|
|
42
99
|
<button type="button" class="cms-studio-btn" title="Toggle Content Studio panel">
|
|
43
100
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="9" y1="9" x2="21" y2="9"/></svg>
|
|
44
101
|
Studio
|
|
@@ -51,12 +108,12 @@ function renderToolbar(navLinks) {
|
|
|
51
108
|
<div class="cms-toolbar-divider"></div>
|
|
52
109
|
<button class="cms-highlight-btn" type="button" title="Show all editable regions">
|
|
53
110
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
|
54
|
-
Show
|
|
111
|
+
<span class="cms-highlight-label">Show all</span>
|
|
55
112
|
</button>
|
|
56
113
|
<div class="cms-toolbar-divider"></div>
|
|
57
|
-
<button class="cms-exit-btn" type="button">
|
|
114
|
+
<button class="cms-exit-btn" type="button" title="Sign out">
|
|
58
115
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
|
59
|
-
|
|
116
|
+
Sign out
|
|
60
117
|
</button>
|
|
61
118
|
</div>
|
|
62
119
|
</div>
|
|
@@ -66,8 +123,9 @@ function renderToolbar(navLinks) {
|
|
|
66
123
|
}
|
|
67
124
|
|
|
68
125
|
export function mountToolbar({ showToast, clearDirty, onLogout }) {
|
|
126
|
+
const staticDelivery = isStaticDelivery();
|
|
69
127
|
const navLinks = getToolbarNavLinks(window.location.pathname);
|
|
70
|
-
const toolbar = renderToolbar(navLinks);
|
|
128
|
+
const toolbar = renderToolbar(navLinks, staticDelivery);
|
|
71
129
|
|
|
72
130
|
const statusDot = toolbar.querySelector('.cms-status-dot');
|
|
73
131
|
const statusText = toolbar.querySelector('.cms-status-text');
|
|
@@ -81,31 +139,123 @@ export function mountToolbar({ showToast, clearDirty, onLogout }) {
|
|
|
81
139
|
}
|
|
82
140
|
}
|
|
83
141
|
|
|
84
|
-
let highlightActive = false;
|
|
85
142
|
const highlightBtn = toolbar.querySelector('.cms-highlight-btn');
|
|
143
|
+
highlightBtn?.addEventListener('click', () =>
|
|
144
|
+
toggleHighlight(highlightBtn, showToast),
|
|
145
|
+
);
|
|
86
146
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
147
|
+
async function draftRequest(method, path) {
|
|
148
|
+
return fetch(buildCmsUrl(path), {
|
|
149
|
+
method,
|
|
150
|
+
headers: { 'Content-Type': 'application/json', 'x-caret-request': '1' },
|
|
151
|
+
credentials: 'same-origin',
|
|
152
|
+
body: method === 'POST' ? '{}' : undefined,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
91
155
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
156
|
+
async function fetchDraftCount() {
|
|
157
|
+
try {
|
|
158
|
+
const res = await fetch(buildCmsUrl('/draft'), {
|
|
159
|
+
credentials: 'same-origin',
|
|
160
|
+
headers: { 'x-caret-request': '1' },
|
|
161
|
+
});
|
|
162
|
+
if (!res.ok) return 0;
|
|
163
|
+
const data = await res.json();
|
|
164
|
+
return typeof data.count === 'number' ? data.count : 0;
|
|
165
|
+
} catch {
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
95
169
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
170
|
+
async function publishDrafts() {
|
|
171
|
+
setStatus('saving', 'Going live…');
|
|
172
|
+
try {
|
|
173
|
+
const res = await draftRequest('POST', '/publish');
|
|
174
|
+
if (!res.ok) throw new Error('publish failed');
|
|
175
|
+
const data = await res.json();
|
|
176
|
+
const n = Array.isArray(data.published) ? data.published.length : 0;
|
|
177
|
+
if (data.rebuild?.triggered && data.rebuild.ok === false) {
|
|
178
|
+
setStatus('error', 'Live, but deploy failed');
|
|
179
|
+
showToast(
|
|
180
|
+
'Changes are saved, but the deploy webhook failed. Check your CI settings.',
|
|
181
|
+
'error',
|
|
182
|
+
);
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if (data.rebuild?.triggered) {
|
|
186
|
+
showToast(
|
|
187
|
+
n > 0
|
|
188
|
+
? `Went live with ${n} change(s) — your site is rebuilding`
|
|
189
|
+
: 'Your site is rebuilding',
|
|
190
|
+
'success',
|
|
191
|
+
);
|
|
192
|
+
} else {
|
|
193
|
+
showToast(n > 0 ? `Went live with ${n} change(s)` : 'Already up to date', 'success');
|
|
194
|
+
}
|
|
195
|
+
window.location.reload();
|
|
196
|
+
return true;
|
|
197
|
+
} catch {
|
|
198
|
+
setStatus('error', 'Go live failed');
|
|
199
|
+
showToast('Go live failed', 'error');
|
|
200
|
+
return false;
|
|
101
201
|
}
|
|
102
|
-
}
|
|
202
|
+
}
|
|
103
203
|
|
|
104
204
|
toolbar.querySelector('.cms-exit-btn')?.addEventListener('click', async () => {
|
|
205
|
+
if (staticDelivery) {
|
|
206
|
+
const count = await fetchDraftCount();
|
|
207
|
+
if (count > 0) {
|
|
208
|
+
const goLive = window.confirm(
|
|
209
|
+
`You have ${count} unpublished change${count === 1 ? '' : 's'}. Go live before signing out?`,
|
|
210
|
+
);
|
|
211
|
+
if (goLive) {
|
|
212
|
+
await publishDrafts();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const discard = window.confirm(
|
|
216
|
+
'Discard unpublished changes and sign out?',
|
|
217
|
+
);
|
|
218
|
+
if (!discard) return;
|
|
219
|
+
try {
|
|
220
|
+
const res = await draftRequest('DELETE', '/draft');
|
|
221
|
+
if (!res.ok) throw new Error('discard failed');
|
|
222
|
+
} catch {
|
|
223
|
+
showToast('Could not discard drafts', 'error');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
105
229
|
clearDirty();
|
|
106
230
|
await onLogout();
|
|
107
231
|
});
|
|
108
232
|
|
|
233
|
+
const publishBtn = toolbar.querySelector('.cms-publish-btn');
|
|
234
|
+
publishBtn?.addEventListener('click', async () => {
|
|
235
|
+
if (
|
|
236
|
+
!window.confirm(
|
|
237
|
+
staticDelivery
|
|
238
|
+
? 'Push all your draft changes to the live site?'
|
|
239
|
+
: 'Publish all your draft changes to the live site?',
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
await publishDrafts();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
toolbar.querySelector('.cms-discard-btn')?.addEventListener('click', async () => {
|
|
248
|
+
if (!window.confirm('Discard all your draft changes? This cannot be undone.')) return;
|
|
249
|
+
try {
|
|
250
|
+
const res = await draftRequest('DELETE', '/draft');
|
|
251
|
+
if (!res.ok) throw new Error('discard failed');
|
|
252
|
+
showToast('Draft discarded', 'success');
|
|
253
|
+
window.location.reload();
|
|
254
|
+
} catch {
|
|
255
|
+
showToast('Discard failed', 'error');
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
109
259
|
const studioButton = toolbar.querySelector('.cms-studio-btn');
|
|
110
260
|
const mapButton = toolbar.querySelector('.cms-map-btn');
|
|
111
261
|
return {
|
package/static/cms/editor.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createSaveField } from './editor/save-queue.js';
|
|
|
16
16
|
import { compressImage, updateEmblaCarousel } from './editor/image-utils.js';
|
|
17
17
|
import { createToast } from './editor/toast.js';
|
|
18
18
|
import { mountEditorGuards } from './editor/guards.js';
|
|
19
|
-
import { mountToolbar } from './editor/toolbar.js';
|
|
19
|
+
import { mountToolbar, normalizePreviewForDelivery } from './editor/toolbar.js';
|
|
20
20
|
import { mountStudioPanel } from './editor/panel.js';
|
|
21
21
|
import { mountStudioSync } from './editor/sync.js';
|
|
22
22
|
import { mountTextEditors } from './editor/text-edit.js';
|
|
@@ -25,17 +25,25 @@ import { mountSectionControls } from './editor/section-controls.js';
|
|
|
25
25
|
import { mountContentMap } from './editor/content-map.js';
|
|
26
26
|
import { mountRichToolbar } from './editor/rich-toolbar.js';
|
|
27
27
|
import { mountLinkFollowAffordances } from './editor/link-follow.js';
|
|
28
|
+
import { revealAndFade } from './editor/highlight.js';
|
|
29
|
+
import { hydrateStega } from './editor/stega-hydrate.js';
|
|
28
30
|
|
|
29
31
|
function redirectToEditorLogin() {
|
|
30
32
|
window.location.href = getEditorLoginUrl(window.location.href);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
function boot() {
|
|
36
|
+
if (normalizePreviewForDelivery()) return;
|
|
37
|
+
|
|
34
38
|
const state = {
|
|
35
39
|
dirtyEl: null,
|
|
36
40
|
linkPopupEl: null,
|
|
37
41
|
};
|
|
38
42
|
|
|
43
|
+
// Promote stega-tagged (loader-fed) content into data-caret bindings before
|
|
44
|
+
// any scanner runs, so prop/component/loop content is click-to-edit too.
|
|
45
|
+
hydrateStega();
|
|
46
|
+
|
|
39
47
|
mountEditorGuards(state);
|
|
40
48
|
mountLinkFollowAffordances();
|
|
41
49
|
|
|
@@ -107,6 +115,15 @@ function boot() {
|
|
|
107
115
|
});
|
|
108
116
|
|
|
109
117
|
mountInlineEditors();
|
|
118
|
+
|
|
119
|
+
// First load after login: flash every editable region once, then fade out.
|
|
120
|
+
// The flag is set by the login form just before it redirects here.
|
|
121
|
+
try {
|
|
122
|
+
if (sessionStorage.getItem('caret:welcome')) {
|
|
123
|
+
sessionStorage.removeItem('caret:welcome');
|
|
124
|
+
revealAndFade(2500);
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {}
|
|
110
127
|
}
|
|
111
128
|
|
|
112
129
|
if (document.readyState === 'loading') {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The authenticated empty-state hint: shown by the middleware on a live page
|
|
3
|
+
* when an editor is signed in but the page has zero `data-caret` bindings — the
|
|
4
|
+
* silent dead-end this replaces (you land on a normal page with no sign the
|
|
5
|
+
* editor noticed you). Self-contained and isolated (`all: initial` on the host)
|
|
6
|
+
* so it can't be restyled by — or restyle — the host page, and so it needs no
|
|
7
|
+
* inline styles (CSP `style-src 'self'` covers this same-origin file).
|
|
8
|
+
*/
|
|
9
|
+
.caret-signin-hint {
|
|
10
|
+
all: initial;
|
|
11
|
+
position: fixed;
|
|
12
|
+
bottom: 16px;
|
|
13
|
+
left: 16px;
|
|
14
|
+
z-index: 2147483000;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 10px;
|
|
19
|
+
max-width: min(92vw, 400px);
|
|
20
|
+
padding: 10px 14px;
|
|
21
|
+
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
22
|
+
font-size: 13px;
|
|
23
|
+
line-height: 1.45;
|
|
24
|
+
color: #e5e7eb;
|
|
25
|
+
background: #111827;
|
|
26
|
+
border: 1px solid #374151;
|
|
27
|
+
border-radius: 10px;
|
|
28
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.caret-signin-hint__dot {
|
|
32
|
+
flex: 0 0 auto;
|
|
33
|
+
width: 8px;
|
|
34
|
+
height: 8px;
|
|
35
|
+
border-radius: 50%;
|
|
36
|
+
background: #34d399;
|
|
37
|
+
box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.18);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.caret-signin-hint__text {
|
|
41
|
+
color: inherit;
|
|
42
|
+
font: inherit;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.caret-signin-hint code {
|
|
46
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
47
|
+
font-size: 0.92em;
|
|
48
|
+
color: #f9fafb;
|
|
49
|
+
background: #1f2937;
|
|
50
|
+
padding: 1px 5px;
|
|
51
|
+
border-radius: 5px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.caret-signin-hint a {
|
|
55
|
+
color: #93c5fd;
|
|
56
|
+
font: inherit;
|
|
57
|
+
text-decoration: underline;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@media (prefers-color-scheme: light) {
|
|
62
|
+
.caret-signin-hint {
|
|
63
|
+
color: #1f2937;
|
|
64
|
+
background: #ffffff;
|
|
65
|
+
border-color: #e5e7eb;
|
|
66
|
+
}
|
|
67
|
+
.caret-signin-hint code {
|
|
68
|
+
color: #111827;
|
|
69
|
+
background: #f3f4f6;
|
|
70
|
+
}
|
|
71
|
+
.caret-signin-hint a {
|
|
72
|
+
color: #2563eb;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -298,3 +298,73 @@
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
.studio-login-footer a:hover { opacity: 0.7; }
|
|
301
|
+
|
|
302
|
+
/* Setup helper (no password / dev password) */
|
|
303
|
+
.studio-login-setup {
|
|
304
|
+
margin-top: 1.25rem;
|
|
305
|
+
padding: 1rem 1.1rem;
|
|
306
|
+
border: 1px solid rgba(127, 127, 127, 0.28);
|
|
307
|
+
border-radius: 10px;
|
|
308
|
+
text-align: left;
|
|
309
|
+
font-size: 0.85rem;
|
|
310
|
+
line-height: 1.55;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.studio-login-setup--dev {
|
|
314
|
+
margin-top: 1rem;
|
|
315
|
+
padding: 0.8rem 1rem;
|
|
316
|
+
border-style: dashed;
|
|
317
|
+
font-size: 0.82rem;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.studio-login-setup-title {
|
|
321
|
+
margin: 0 0 0.6rem;
|
|
322
|
+
font-weight: 600;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.studio-login-setup ol {
|
|
326
|
+
margin: 0;
|
|
327
|
+
padding-left: 1.2rem;
|
|
328
|
+
display: grid;
|
|
329
|
+
gap: 0.45rem;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.studio-login-setup-snippet-row {
|
|
333
|
+
display: flex;
|
|
334
|
+
gap: 0.4rem;
|
|
335
|
+
align-items: center;
|
|
336
|
+
margin-top: 0.35rem;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.studio-login-setup-snippet {
|
|
340
|
+
flex: 1;
|
|
341
|
+
padding: 0.4rem 0.55rem;
|
|
342
|
+
border-radius: 6px;
|
|
343
|
+
background: rgba(0, 0, 0, 0.28);
|
|
344
|
+
overflow: auto;
|
|
345
|
+
white-space: nowrap;
|
|
346
|
+
font-family: var(--studio-font-mono);
|
|
347
|
+
font-size: 0.85em;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.studio-login-setup-copy {
|
|
351
|
+
appearance: none;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
border: 1px solid currentColor;
|
|
354
|
+
background: transparent;
|
|
355
|
+
color: inherit;
|
|
356
|
+
border-radius: 6px;
|
|
357
|
+
padding: 0.4rem 0.65rem;
|
|
358
|
+
font-family: var(--studio-font);
|
|
359
|
+
font-size: 0.85em;
|
|
360
|
+
font-weight: 500;
|
|
361
|
+
line-height: 1.2;
|
|
362
|
+
flex-shrink: 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.studio-login-setup-copy:hover { opacity: 0.85; }
|
|
366
|
+
|
|
367
|
+
.studio-login-setup-link {
|
|
368
|
+
display: inline-block;
|
|
369
|
+
margin-top: 0.7rem;
|
|
370
|
+
}
|