@caretcms/core 0.1.1 → 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 +82 -17
- package/dist/browser-runtime.d.ts +16 -0
- package/dist/browser-runtime.d.ts.map +1 -1
- package/dist/browser-runtime.js +8 -3
- package/dist/browser-runtime.js.map +1 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -3
- package/dist/index.js.map +1 -1
- package/dist/runtime/providers.d.ts +9 -0
- package/dist/runtime/providers.d.ts.map +1 -1
- package/dist/runtime/providers.js +6 -0
- package/dist/runtime/providers.js.map +1 -1
- package/dist/runtime/publish.d.ts +2 -0
- package/dist/runtime/publish.d.ts.map +1 -1
- package/dist/runtime/publish.js +4 -0
- package/dist/runtime/publish.js.map +1 -1
- 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/rewrite.d.ts +18 -0
- package/dist/runtime/rewrite.d.ts.map +1 -1
- package/dist/runtime/rewrite.js +9 -1
- package/dist/runtime/rewrite.js.map +1 -1
- package/dist/runtime/rich-allowlist.d.ts +3 -2
- package/dist/runtime/rich-allowlist.d.ts.map +1 -1
- package/dist/runtime/rich-allowlist.js +9 -3
- package/dist/runtime/rich-allowlist.js.map +1 -1
- package/dist/runtime/routes/_helpers.d.ts +7 -0
- package/dist/runtime/routes/_helpers.d.ts.map +1 -1
- package/dist/runtime/routes/_helpers.js +34 -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 +15 -12
- 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 +1 -0
- 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 +1 -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 +3 -2
- package/dist/runtime/routes/collections-metadata.js.map +1 -1
- package/dist/runtime/routes/draft.d.ts +5 -0
- package/dist/runtime/routes/draft.d.ts.map +1 -1
- package/dist/runtime/routes/draft.js +18 -1
- package/dist/runtime/routes/draft.js.map +1 -1
- 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 +4 -3
- 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 +3 -2
- package/dist/runtime/routes/mutate.js.map +1 -1
- package/dist/runtime/routes/publish.d.ts +1 -0
- package/dist/runtime/routes/publish.d.ts.map +1 -1
- package/dist/runtime/routes/publish.js +11 -2
- package/dist/runtime/routes/publish.js.map +1 -1
- 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 +3 -2
- 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 +1 -0
- package/dist/runtime/routes/upload.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/storage/frontmatter-codec.d.ts.map +1 -1
- package/dist/runtime/storage/frontmatter-codec.js +8 -0
- package/dist/runtime/storage/frontmatter-codec.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/package.json +1 -1
- package/static/cms/dev-toolbar/app.js +3 -1
- package/static/cms/editor/config.js +10 -0
- package/static/cms/editor/helpers.js +5 -1
- package/static/cms/editor/highlight.js +4 -6
- package/static/cms/editor/sanitize.js +9 -5
- package/static/cms/editor/toolbar.css +60 -5
- package/static/cms/editor/toolbar.js +133 -65
- package/static/cms/editor.js +3 -1
- package/static/cms/studio-login.css +70 -0
- package/static/cms/studio.css +80 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toggleHighlight } from './highlight.js';
|
|
2
|
-
import { buildCmsUrl } from './config.js';
|
|
2
|
+
import { buildCmsUrl, isStaticDelivery } from './config.js';
|
|
3
3
|
|
|
4
4
|
const PREVIEW_COOKIE = 'caret_preview';
|
|
5
5
|
|
|
@@ -13,6 +13,26 @@ function setPreviewCookie(on) {
|
|
|
13
13
|
: `${PREVIEW_COOKIE}=; path=/; Max-Age=0; SameSite=Lax`;
|
|
14
14
|
}
|
|
15
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
|
+
|
|
16
36
|
function getToolbarNavLinks(pagePath) {
|
|
17
37
|
const links = [];
|
|
18
38
|
const headerNav = document.querySelector('#main-header nav');
|
|
@@ -29,7 +49,22 @@ function getToolbarNavLinks(pagePath) {
|
|
|
29
49
|
return links;
|
|
30
50
|
}
|
|
31
51
|
|
|
32
|
-
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) {
|
|
33
68
|
const navLinksHtml = navLinks
|
|
34
69
|
.map(
|
|
35
70
|
({ href, label, isActive }) =>
|
|
@@ -37,14 +72,20 @@ function renderToolbar(navLinks) {
|
|
|
37
72
|
)
|
|
38
73
|
.join('');
|
|
39
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
|
+
|
|
40
80
|
const toolbar = document.createElement('div');
|
|
41
81
|
toolbar.className = 'cms-toolbar';
|
|
82
|
+
toolbar.dataset.delivery = staticDelivery ? 'static' : 'server';
|
|
42
83
|
toolbar.innerHTML = `
|
|
43
84
|
<div class="cms-toolbar-inner">
|
|
44
85
|
<div class="cms-toolbar-left">
|
|
45
|
-
<div class="cms-toolbar-badge">
|
|
86
|
+
<div class="cms-toolbar-badge" title="${badgeHint}">
|
|
46
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>
|
|
47
|
-
<span class="cms-toolbar-badge-text"
|
|
88
|
+
<span class="cms-toolbar-badge-text">${badgeLabel}</span>
|
|
48
89
|
</div>
|
|
49
90
|
<div class="cms-toolbar-divider"></div>
|
|
50
91
|
<div class="cms-status-group">
|
|
@@ -54,19 +95,7 @@ function renderToolbar(navLinks) {
|
|
|
54
95
|
</div>
|
|
55
96
|
${navLinksHtml ? `<div class="cms-toolbar-nav">${navLinksHtml}</div>` : ''}
|
|
56
97
|
<div class="cms-toolbar-right">
|
|
57
|
-
|
|
58
|
-
<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="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
59
|
-
Preview
|
|
60
|
-
</button>
|
|
61
|
-
<button class="cms-publish-btn" type="button" title="Publish your draft changes to the live site" hidden>
|
|
62
|
-
<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>
|
|
63
|
-
Publish
|
|
64
|
-
</button>
|
|
65
|
-
<button class="cms-discard-btn" type="button" title="Discard your draft changes" hidden>
|
|
66
|
-
<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>
|
|
67
|
-
Discard
|
|
68
|
-
</button>
|
|
69
|
-
<div class="cms-toolbar-divider"></div>
|
|
98
|
+
${renderDraftControls(staticDelivery)}
|
|
70
99
|
<button type="button" class="cms-studio-btn" title="Toggle Content Studio panel">
|
|
71
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>
|
|
72
101
|
Studio
|
|
@@ -79,12 +108,12 @@ function renderToolbar(navLinks) {
|
|
|
79
108
|
<div class="cms-toolbar-divider"></div>
|
|
80
109
|
<button class="cms-highlight-btn" type="button" title="Show all editable regions">
|
|
81
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>
|
|
82
|
-
Show
|
|
111
|
+
<span class="cms-highlight-label">Show all</span>
|
|
83
112
|
</button>
|
|
84
113
|
<div class="cms-toolbar-divider"></div>
|
|
85
|
-
<button class="cms-exit-btn" type="button">
|
|
114
|
+
<button class="cms-exit-btn" type="button" title="Sign out">
|
|
86
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>
|
|
87
|
-
|
|
116
|
+
Sign out
|
|
88
117
|
</button>
|
|
89
118
|
</div>
|
|
90
119
|
</div>
|
|
@@ -94,8 +123,9 @@ function renderToolbar(navLinks) {
|
|
|
94
123
|
}
|
|
95
124
|
|
|
96
125
|
export function mountToolbar({ showToast, clearDirty, onLogout }) {
|
|
126
|
+
const staticDelivery = isStaticDelivery();
|
|
97
127
|
const navLinks = getToolbarNavLinks(window.location.pathname);
|
|
98
|
-
const toolbar = renderToolbar(navLinks);
|
|
128
|
+
const toolbar = renderToolbar(navLinks, staticDelivery);
|
|
99
129
|
|
|
100
130
|
const statusDot = toolbar.querySelector('.cms-status-dot');
|
|
101
131
|
const statusText = toolbar.querySelector('.cms-status-text');
|
|
@@ -114,40 +144,6 @@ export function mountToolbar({ showToast, clearDirty, onLogout }) {
|
|
|
114
144
|
toggleHighlight(highlightBtn, showToast),
|
|
115
145
|
);
|
|
116
146
|
|
|
117
|
-
toolbar.querySelector('.cms-exit-btn')?.addEventListener('click', async () => {
|
|
118
|
-
clearDirty();
|
|
119
|
-
await onLogout();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// --- Draft preview + publish/discard ---
|
|
123
|
-
const previewBtn = toolbar.querySelector('.cms-preview-btn');
|
|
124
|
-
const publishBtn = toolbar.querySelector('.cms-publish-btn');
|
|
125
|
-
const discardBtn = toolbar.querySelector('.cms-discard-btn');
|
|
126
|
-
const badgeText = toolbar.querySelector('.cms-toolbar-badge-text');
|
|
127
|
-
|
|
128
|
-
function reflectPreview() {
|
|
129
|
-
const on = previewActive();
|
|
130
|
-
previewBtn?.classList.toggle('cms-preview-btn-active', on);
|
|
131
|
-
if (publishBtn) publishBtn.hidden = !on;
|
|
132
|
-
if (discardBtn) discardBtn.hidden = !on;
|
|
133
|
-
if (badgeText) badgeText.textContent = on ? 'Draft' : 'Editor';
|
|
134
|
-
const label = previewBtn
|
|
135
|
-
&& Array.from(previewBtn.childNodes).find((n) => n.nodeType === 3 && n.textContent?.trim());
|
|
136
|
-
if (label) label.textContent = on ? ' Previewing' : ' Preview';
|
|
137
|
-
}
|
|
138
|
-
reflectPreview();
|
|
139
|
-
|
|
140
|
-
previewBtn?.addEventListener('click', () => {
|
|
141
|
-
const turningOn = !previewActive();
|
|
142
|
-
setPreviewCookie(turningOn);
|
|
143
|
-
showToast(
|
|
144
|
-
turningOn ? 'Draft preview on — your edits stay unpublished' : 'Draft preview off',
|
|
145
|
-
'success',
|
|
146
|
-
);
|
|
147
|
-
// Reload so the server installs (or drops) the per-editor draft overlay.
|
|
148
|
-
window.location.reload();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
147
|
async function draftRequest(method, path) {
|
|
152
148
|
return fetch(buildCmsUrl(path), {
|
|
153
149
|
method,
|
|
@@ -157,30 +153,102 @@ export function mountToolbar({ showToast, clearDirty, onLogout }) {
|
|
|
157
153
|
});
|
|
158
154
|
}
|
|
159
155
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
+
}
|
|
169
|
+
|
|
170
|
+
async function publishDrafts() {
|
|
171
|
+
setStatus('saving', 'Going live…');
|
|
163
172
|
try {
|
|
164
173
|
const res = await draftRequest('POST', '/publish');
|
|
165
174
|
if (!res.ok) throw new Error('publish failed');
|
|
166
175
|
const data = await res.json();
|
|
167
176
|
const n = Array.isArray(data.published) ? data.published.length : 0;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
+
}
|
|
171
195
|
window.location.reload();
|
|
196
|
+
return true;
|
|
172
197
|
} catch {
|
|
173
|
-
setStatus('error', '
|
|
174
|
-
showToast('
|
|
198
|
+
setStatus('error', 'Go live failed');
|
|
199
|
+
showToast('Go live failed', 'error');
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
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
|
+
|
|
229
|
+
clearDirty();
|
|
230
|
+
await onLogout();
|
|
231
|
+
});
|
|
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;
|
|
175
243
|
}
|
|
244
|
+
await publishDrafts();
|
|
176
245
|
});
|
|
177
246
|
|
|
178
|
-
|
|
247
|
+
toolbar.querySelector('.cms-discard-btn')?.addEventListener('click', async () => {
|
|
179
248
|
if (!window.confirm('Discard all your draft changes? This cannot be undone.')) return;
|
|
180
249
|
try {
|
|
181
250
|
const res = await draftRequest('DELETE', '/draft');
|
|
182
251
|
if (!res.ok) throw new Error('discard failed');
|
|
183
|
-
setPreviewCookie(false);
|
|
184
252
|
showToast('Draft discarded', 'success');
|
|
185
253
|
window.location.reload();
|
|
186
254
|
} catch {
|
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';
|
|
@@ -33,6 +33,8 @@ function redirectToEditorLogin() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function boot() {
|
|
36
|
+
if (normalizePreviewForDelivery()) return;
|
|
37
|
+
|
|
36
38
|
const state = {
|
|
37
39
|
dirtyEl: null,
|
|
38
40
|
linkPopupEl: null,
|
|
@@ -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
|
+
}
|
package/static/cms/studio.css
CHANGED
|
@@ -55,7 +55,6 @@ a {
|
|
|
55
55
|
.studio-header {
|
|
56
56
|
display: flex;
|
|
57
57
|
align-items: center;
|
|
58
|
-
justify-content: space-between;
|
|
59
58
|
gap: var(--space-theme-md);
|
|
60
59
|
padding: var(--space-theme-md) var(--space-theme-lg);
|
|
61
60
|
border-bottom: 1px solid var(--studio-border);
|
|
@@ -65,6 +64,29 @@ a {
|
|
|
65
64
|
z-index: 10;
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
.studio-breadcrumb {
|
|
68
|
+
flex: 1;
|
|
69
|
+
min-width: 0;
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: var(--space-theme-sm);
|
|
73
|
+
font-family: var(--studio-font);
|
|
74
|
+
font-size: 11px;
|
|
75
|
+
font-weight: 500;
|
|
76
|
+
line-height: 1.2;
|
|
77
|
+
letter-spacing: 0.08em;
|
|
78
|
+
text-transform: uppercase;
|
|
79
|
+
color: var(--studio-text-dim);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.studio-header-nav {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: var(--space-theme-xs);
|
|
86
|
+
margin-left: auto;
|
|
87
|
+
flex-shrink: 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
68
90
|
.studio-brand {
|
|
69
91
|
display: flex;
|
|
70
92
|
align-items: center;
|
|
@@ -73,24 +95,18 @@ a {
|
|
|
73
95
|
font-size: 1rem;
|
|
74
96
|
letter-spacing: 0.02em;
|
|
75
97
|
color: var(--studio-text);
|
|
98
|
+
transition: color 0.15s;
|
|
99
|
+
flex-shrink: 0;
|
|
76
100
|
}
|
|
77
101
|
|
|
102
|
+
.studio-brand:hover { color: var(--studio-text-muted); }
|
|
103
|
+
|
|
78
104
|
.studio-brand-mark {
|
|
79
105
|
width: 24px;
|
|
80
106
|
height: 24px;
|
|
81
107
|
color: var(--studio-accent);
|
|
82
108
|
}
|
|
83
109
|
|
|
84
|
-
.studio-breadcrumb {
|
|
85
|
-
display: flex;
|
|
86
|
-
align-items: center;
|
|
87
|
-
gap: var(--space-theme-sm);
|
|
88
|
-
font-size: 11px;
|
|
89
|
-
letter-spacing: 0.08em;
|
|
90
|
-
text-transform: uppercase;
|
|
91
|
-
color: var(--studio-text-dim);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
110
|
.studio-breadcrumb a:hover { color: var(--studio-text-muted); }
|
|
95
111
|
.studio-breadcrumb-sep { opacity: 0.4; }
|
|
96
112
|
.studio-breadcrumb-current { color: var(--studio-text-muted); }
|
|
@@ -101,6 +117,37 @@ a {
|
|
|
101
117
|
overflow-y: auto;
|
|
102
118
|
}
|
|
103
119
|
|
|
120
|
+
.studio-home {
|
|
121
|
+
max-width: 64rem;
|
|
122
|
+
margin: 0 auto;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.studio-page-intro {
|
|
126
|
+
text-align: center;
|
|
127
|
+
margin: 0 0 2.5rem;
|
|
128
|
+
font-size: 0.85rem;
|
|
129
|
+
color: var(--studio-text-dim);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.studio-home-grid {
|
|
133
|
+
display: grid;
|
|
134
|
+
gap: 1rem;
|
|
135
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@media (max-width: 640px) {
|
|
139
|
+
.studio-header {
|
|
140
|
+
flex-wrap: wrap;
|
|
141
|
+
padding: var(--space-theme-sm) var(--space-theme-md);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.studio-breadcrumb {
|
|
145
|
+
order: 3;
|
|
146
|
+
flex-basis: 100%;
|
|
147
|
+
padding-top: var(--space-theme-xs);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
104
151
|
/* ─── Drop zone ─────────────────────────────────────────────────────── */
|
|
105
152
|
|
|
106
153
|
.studio-dropzone {
|
|
@@ -259,9 +306,14 @@ textarea.studio-input {
|
|
|
259
306
|
/* ─── Header nav links ──────────────────────────────────────────────── */
|
|
260
307
|
|
|
261
308
|
.studio-nav-link {
|
|
309
|
+
display: inline-flex;
|
|
310
|
+
align-items: center;
|
|
262
311
|
padding: 4px 12px;
|
|
263
|
-
font-
|
|
264
|
-
|
|
312
|
+
font-family: var(--studio-font);
|
|
313
|
+
font-size: 11px;
|
|
314
|
+
font-weight: 500;
|
|
315
|
+
line-height: 1.2;
|
|
316
|
+
letter-spacing: 0.08em;
|
|
265
317
|
text-transform: uppercase;
|
|
266
318
|
text-decoration: none;
|
|
267
319
|
color: var(--studio-text-dim);
|
|
@@ -269,6 +321,21 @@ textarea.studio-input {
|
|
|
269
321
|
border-radius: var(--radius-theme-xs);
|
|
270
322
|
}
|
|
271
323
|
|
|
324
|
+
button.studio-nav-link {
|
|
325
|
+
appearance: none;
|
|
326
|
+
border: none;
|
|
327
|
+
cursor: pointer;
|
|
328
|
+
background: transparent;
|
|
329
|
+
margin: 0;
|
|
330
|
+
font-family: var(--studio-font);
|
|
331
|
+
font-size: 11px;
|
|
332
|
+
font-weight: 500;
|
|
333
|
+
line-height: 1.2;
|
|
334
|
+
letter-spacing: 0.08em;
|
|
335
|
+
text-transform: uppercase;
|
|
336
|
+
color: var(--studio-text-dim);
|
|
337
|
+
}
|
|
338
|
+
|
|
272
339
|
.studio-nav-link:hover {
|
|
273
340
|
color: var(--studio-text-muted);
|
|
274
341
|
background: var(--color-theme-surface-muted);
|