@caretcms/core 0.1.1 → 0.2.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.
Files changed (222) hide show
  1. package/README.md +101 -18
  2. package/dist/browser-runtime.d.ts +16 -0
  3. package/dist/browser-runtime.d.ts.map +1 -1
  4. package/dist/browser-runtime.js +8 -3
  5. package/dist/browser-runtime.js.map +1 -1
  6. package/dist/contracts.d.ts +1 -0
  7. package/dist/contracts.d.ts.map +1 -1
  8. package/dist/contracts.js +1 -0
  9. package/dist/contracts.js.map +1 -1
  10. package/dist/index.d.ts +34 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +239 -13
  13. package/dist/index.js.map +1 -1
  14. package/dist/loader.d.ts +17 -6
  15. package/dist/loader.d.ts.map +1 -1
  16. package/dist/loader.js +33 -14
  17. package/dist/loader.js.map +1 -1
  18. package/dist/markdown/block-context.d.ts +21 -0
  19. package/dist/markdown/block-context.d.ts.map +1 -0
  20. package/dist/markdown/block-context.js +27 -0
  21. package/dist/markdown/block-context.js.map +1 -0
  22. package/dist/markdown/canonical-body.d.ts +30 -0
  23. package/dist/markdown/canonical-body.d.ts.map +1 -0
  24. package/dist/markdown/canonical-body.js +54 -0
  25. package/dist/markdown/canonical-body.js.map +1 -0
  26. package/dist/markdown/contracts.d.ts +66 -0
  27. package/dist/markdown/contracts.d.ts.map +1 -0
  28. package/dist/markdown/contracts.js +102 -0
  29. package/dist/markdown/contracts.js.map +1 -0
  30. package/dist/markdown/html-to-snodes.d.ts +26 -0
  31. package/dist/markdown/html-to-snodes.d.ts.map +1 -0
  32. package/dist/markdown/html-to-snodes.js +118 -0
  33. package/dist/markdown/html-to-snodes.js.map +1 -0
  34. package/dist/markdown/remark.d.ts +35 -0
  35. package/dist/markdown/remark.d.ts.map +1 -0
  36. package/dist/markdown/remark.js +66 -0
  37. package/dist/markdown/remark.js.map +1 -0
  38. package/dist/markdown/satteri.d.ts +19 -0
  39. package/dist/markdown/satteri.d.ts.map +1 -0
  40. package/dist/markdown/satteri.js +120 -0
  41. package/dist/markdown/satteri.js.map +1 -0
  42. package/dist/markdown/serialize.d.ts +60 -0
  43. package/dist/markdown/serialize.d.ts.map +1 -0
  44. package/dist/markdown/serialize.js +264 -0
  45. package/dist/markdown/serialize.js.map +1 -0
  46. package/dist/markdown/splice.d.ts +32 -0
  47. package/dist/markdown/splice.d.ts.map +1 -0
  48. package/dist/markdown/splice.js +44 -0
  49. package/dist/markdown/splice.js.map +1 -0
  50. package/dist/markdown/stamp.d.ts +68 -0
  51. package/dist/markdown/stamp.d.ts.map +1 -0
  52. package/dist/markdown/stamp.js +131 -0
  53. package/dist/markdown/stamp.js.map +1 -0
  54. package/dist/runtime/auth/rate-limiter.d.ts.map +1 -1
  55. package/dist/runtime/auth/rate-limiter.js +5 -0
  56. package/dist/runtime/auth/rate-limiter.js.map +1 -1
  57. package/dist/runtime/auth/session.d.ts +2 -3
  58. package/dist/runtime/auth/session.d.ts.map +1 -1
  59. package/dist/runtime/auth/session.js +52 -21
  60. package/dist/runtime/auth/session.js.map +1 -1
  61. package/dist/runtime/content.d.ts.map +1 -1
  62. package/dist/runtime/content.js +4 -2
  63. package/dist/runtime/content.js.map +1 -1
  64. package/dist/runtime/middleware.d.ts.map +1 -1
  65. package/dist/runtime/middleware.js +21 -4
  66. package/dist/runtime/middleware.js.map +1 -1
  67. package/dist/runtime/mutations/contracts.d.ts +25 -1
  68. package/dist/runtime/mutations/contracts.d.ts.map +1 -1
  69. package/dist/runtime/mutations/contracts.js +67 -0
  70. package/dist/runtime/mutations/contracts.js.map +1 -1
  71. package/dist/runtime/mutations/engine.d.ts +6 -1
  72. package/dist/runtime/mutations/engine.d.ts.map +1 -1
  73. package/dist/runtime/mutations/engine.js +151 -42
  74. package/dist/runtime/mutations/engine.js.map +1 -1
  75. package/dist/runtime/providers.d.ts +9 -0
  76. package/dist/runtime/providers.d.ts.map +1 -1
  77. package/dist/runtime/providers.js +6 -0
  78. package/dist/runtime/providers.js.map +1 -1
  79. package/dist/runtime/publish.d.ts +15 -1
  80. package/dist/runtime/publish.d.ts.map +1 -1
  81. package/dist/runtime/publish.js +79 -4
  82. package/dist/runtime/publish.js.map +1 -1
  83. package/dist/runtime/rebuild-webhook.d.ts +29 -0
  84. package/dist/runtime/rebuild-webhook.d.ts.map +1 -0
  85. package/dist/runtime/rebuild-webhook.js +37 -0
  86. package/dist/runtime/rebuild-webhook.js.map +1 -0
  87. package/dist/runtime/request-context.d.ts +9 -1
  88. package/dist/runtime/request-context.d.ts.map +1 -1
  89. package/dist/runtime/request-context.js.map +1 -1
  90. package/dist/runtime/rewrite.d.ts +18 -0
  91. package/dist/runtime/rewrite.d.ts.map +1 -1
  92. package/dist/runtime/rewrite.js +118 -4
  93. package/dist/runtime/rewrite.js.map +1 -1
  94. package/dist/runtime/rich-allowlist.d.ts +3 -2
  95. package/dist/runtime/rich-allowlist.d.ts.map +1 -1
  96. package/dist/runtime/rich-allowlist.js +14 -4
  97. package/dist/runtime/rich-allowlist.js.map +1 -1
  98. package/dist/runtime/routes/_helpers.d.ts +7 -0
  99. package/dist/runtime/routes/_helpers.d.ts.map +1 -1
  100. package/dist/runtime/routes/_helpers.js +34 -1
  101. package/dist/runtime/routes/_helpers.js.map +1 -1
  102. package/dist/runtime/routes/admin-entry.d.ts +1 -0
  103. package/dist/runtime/routes/admin-entry.d.ts.map +1 -1
  104. package/dist/runtime/routes/admin-entry.js +3 -2
  105. package/dist/runtime/routes/admin-entry.js.map +1 -1
  106. package/dist/runtime/routes/admin.d.ts +1 -0
  107. package/dist/runtime/routes/admin.d.ts.map +1 -1
  108. package/dist/runtime/routes/admin.js +20 -13
  109. package/dist/runtime/routes/admin.js.map +1 -1
  110. package/dist/runtime/routes/auth-login.d.ts +1 -0
  111. package/dist/runtime/routes/auth-login.d.ts.map +1 -1
  112. package/dist/runtime/routes/auth-login.js +1 -0
  113. package/dist/runtime/routes/auth-login.js.map +1 -1
  114. package/dist/runtime/routes/auth-logout.d.ts +1 -0
  115. package/dist/runtime/routes/auth-logout.d.ts.map +1 -1
  116. package/dist/runtime/routes/auth-logout.js +1 -0
  117. package/dist/runtime/routes/auth-logout.js.map +1 -1
  118. package/dist/runtime/routes/auth-session.d.ts +1 -0
  119. package/dist/runtime/routes/auth-session.d.ts.map +1 -1
  120. package/dist/runtime/routes/auth-session.js +1 -0
  121. package/dist/runtime/routes/auth-session.js.map +1 -1
  122. package/dist/runtime/routes/collection-list.d.ts +1 -0
  123. package/dist/runtime/routes/collection-list.d.ts.map +1 -1
  124. package/dist/runtime/routes/collection-list.js +3 -2
  125. package/dist/runtime/routes/collection-list.js.map +1 -1
  126. package/dist/runtime/routes/collections-metadata.d.ts +1 -0
  127. package/dist/runtime/routes/collections-metadata.d.ts.map +1 -1
  128. package/dist/runtime/routes/collections-metadata.js +3 -2
  129. package/dist/runtime/routes/collections-metadata.js.map +1 -1
  130. package/dist/runtime/routes/draft.d.ts +5 -0
  131. package/dist/runtime/routes/draft.d.ts.map +1 -1
  132. package/dist/runtime/routes/draft.js +18 -1
  133. package/dist/runtime/routes/draft.js.map +1 -1
  134. package/dist/runtime/routes/editor-assets.d.ts +1 -0
  135. package/dist/runtime/routes/editor-assets.d.ts.map +1 -1
  136. package/dist/runtime/routes/editor-assets.js +1 -0
  137. package/dist/runtime/routes/editor-assets.js.map +1 -1
  138. package/dist/runtime/routes/entries.d.ts +1 -0
  139. package/dist/runtime/routes/entries.d.ts.map +1 -1
  140. package/dist/runtime/routes/entries.js +6 -4
  141. package/dist/runtime/routes/entries.js.map +1 -1
  142. package/dist/runtime/routes/history.d.ts +1 -0
  143. package/dist/runtime/routes/history.d.ts.map +1 -1
  144. package/dist/runtime/routes/history.js +50 -24
  145. package/dist/runtime/routes/history.js.map +1 -1
  146. package/dist/runtime/routes/mutate.d.ts +1 -0
  147. package/dist/runtime/routes/mutate.d.ts.map +1 -1
  148. package/dist/runtime/routes/mutate.js +42 -7
  149. package/dist/runtime/routes/mutate.js.map +1 -1
  150. package/dist/runtime/routes/publish.d.ts +1 -0
  151. package/dist/runtime/routes/publish.d.ts.map +1 -1
  152. package/dist/runtime/routes/publish.js +15 -3
  153. package/dist/runtime/routes/publish.js.map +1 -1
  154. package/dist/runtime/routes/schema.d.ts +1 -0
  155. package/dist/runtime/routes/schema.d.ts.map +1 -1
  156. package/dist/runtime/routes/schema.js +7 -3
  157. package/dist/runtime/routes/schema.js.map +1 -1
  158. package/dist/runtime/routes/studio-home.d.ts +1 -0
  159. package/dist/runtime/routes/studio-home.d.ts.map +1 -1
  160. package/dist/runtime/routes/studio-home.js +10 -9
  161. package/dist/runtime/routes/studio-home.js.map +1 -1
  162. package/dist/runtime/routes/theme-stylesheet.d.ts +1 -0
  163. package/dist/runtime/routes/theme-stylesheet.d.ts.map +1 -1
  164. package/dist/runtime/routes/theme-stylesheet.js +12 -1
  165. package/dist/runtime/routes/theme-stylesheet.js.map +1 -1
  166. package/dist/runtime/routes/upload.d.ts +1 -0
  167. package/dist/runtime/routes/upload.d.ts.map +1 -1
  168. package/dist/runtime/routes/upload.js +1 -0
  169. package/dist/runtime/routes/upload.js.map +1 -1
  170. package/dist/runtime/static-bake.d.ts +20 -0
  171. package/dist/runtime/static-bake.d.ts.map +1 -0
  172. package/dist/runtime/static-bake.js +58 -0
  173. package/dist/runtime/static-bake.js.map +1 -0
  174. package/dist/runtime/storage/atomic-write.d.ts.map +1 -1
  175. package/dist/runtime/storage/atomic-write.js +8 -3
  176. package/dist/runtime/storage/atomic-write.js.map +1 -1
  177. package/dist/runtime/storage/frontmatter-codec.d.ts.map +1 -1
  178. package/dist/runtime/storage/frontmatter-codec.js +8 -0
  179. package/dist/runtime/storage/frontmatter-codec.js.map +1 -1
  180. package/dist/runtime/storage/markdown-adapter.d.ts +19 -0
  181. package/dist/runtime/storage/markdown-adapter.d.ts.map +1 -1
  182. package/dist/runtime/storage/markdown-adapter.js +41 -0
  183. package/dist/runtime/storage/markdown-adapter.js.map +1 -1
  184. package/dist/runtime/storage/session-overlay-adapter.d.ts +8 -0
  185. package/dist/runtime/storage/session-overlay-adapter.d.ts.map +1 -1
  186. package/dist/runtime/storage/session-overlay-adapter.js +24 -2
  187. package/dist/runtime/storage/session-overlay-adapter.js.map +1 -1
  188. package/dist/runtime/utils.d.ts +10 -0
  189. package/dist/runtime/utils.d.ts.map +1 -1
  190. package/dist/runtime/utils.js +16 -0
  191. package/dist/runtime/utils.js.map +1 -1
  192. package/dist/runtime/views/studio-layout.d.ts.map +1 -1
  193. package/dist/runtime/views/studio-layout.js +11 -10
  194. package/dist/runtime/views/studio-layout.js.map +1 -1
  195. package/dist/types.d.ts +40 -0
  196. package/dist/types.d.ts.map +1 -1
  197. package/package.json +2 -2
  198. package/static/cms/admin-entry.js +36 -2
  199. package/static/cms/dev-toolbar/app.js +3 -1
  200. package/static/cms/editor/config.js +10 -0
  201. package/static/cms/editor/guards.js +1 -1
  202. package/static/cms/editor/helpers.js +5 -1
  203. package/static/cms/editor/highlight.js +4 -6
  204. package/static/cms/editor/image-edit.js +41 -11
  205. package/static/cms/editor/image.css +7 -1
  206. package/static/cms/editor/md-block-edit.js +176 -0
  207. package/static/cms/editor/md-serialize.js +301 -0
  208. package/static/cms/editor/panel.css +69 -25
  209. package/static/cms/editor/panel.js +39 -0
  210. package/static/cms/editor/rich-toolbar.js +4 -3
  211. package/static/cms/editor/sanitize.js +10 -5
  212. package/static/cms/editor/sync.js +1 -1
  213. package/static/cms/editor/text-edit.js +68 -17
  214. package/static/cms/editor/text-link-interactions.js +4 -4
  215. package/static/cms/editor/toast.css +114 -2
  216. package/static/cms/editor/toast.js +121 -22
  217. package/static/cms/editor/tokens-and-text.css +64 -3
  218. package/static/cms/editor/toolbar.css +76 -8
  219. package/static/cms/editor/toolbar.js +173 -64
  220. package/static/cms/editor.js +16 -3
  221. package/static/cms/studio-login.css +70 -0
  222. package/static/cms/studio.css +80 -13
@@ -1,29 +1,128 @@
1
+ const SUCCESS_SVG =
2
+ '<svg class="cms-toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>';
3
+ const ERROR_SVG =
4
+ '<svg class="cms-toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>';
5
+ const WARNING_SVG =
6
+ '<svg class="cms-toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>';
7
+
8
+ function iconFor(type) {
9
+ if (type === 'success') return SUCCESS_SVG;
10
+ if (type === 'warning') return WARNING_SVG;
11
+ return ERROR_SVG;
12
+ }
13
+
14
+ /**
15
+ * Toast controller. Returns a `showToast(message, type)` function with a
16
+ * `.conflict()` method for the two-choice save-conflict prompt.
17
+ *
18
+ * Design notes:
19
+ * - Toasts stack (a container region) instead of one replacing the next, so a
20
+ * burst of saves or an error + a later success don't clobber each other.
21
+ * - Errors and conflict prompts are STICKY and dismissible — a 2.5s auto-hide
22
+ * would let a failure or a data-loss choice vanish before it's read.
23
+ * - The message is always set via textContent, never innerHTML, so a
24
+ * server-provided error string can't inject markup into the editor chrome.
25
+ * - The container is an aria-live region; errors/conflicts are role="alert".
26
+ */
1
27
  export function createToast() {
2
- let activeToast = null;
28
+ let container = null;
3
29
 
4
- return function showToast(message, type) {
5
- if (activeToast) {
6
- activeToast.remove();
7
- }
30
+ function ensureContainer() {
31
+ if (container && document.body.contains(container)) return container;
32
+ container = document.createElement('div');
33
+ container.className = 'cms-toast-stack';
34
+ container.setAttribute('aria-live', 'polite');
35
+ document.body.appendChild(container);
36
+ return container;
37
+ }
38
+
39
+ function dismiss(toast) {
40
+ if (toast._caretDismissed) return;
41
+ toast._caretDismissed = true;
42
+ toast.classList.add('cms-toast-out');
43
+ setTimeout(() => toast.remove(), 300);
44
+ }
45
+
46
+ function render(type, { message, sticky, actions, role }) {
47
+ const stack = ensureContainer();
8
48
 
9
49
  const toast = document.createElement('div');
10
50
  toast.className = `cms-toast cms-toast-${type}`;
51
+ if (role) toast.setAttribute('role', role);
52
+ if (sticky || (actions && actions.length)) toast.classList.add('cms-toast-interactive');
53
+
54
+ const iconWrap = document.createElement('span');
55
+ iconWrap.className = 'cms-toast-icon-wrap';
56
+ iconWrap.setAttribute('aria-hidden', 'true');
57
+ iconWrap.innerHTML = iconFor(type); // trusted constant SVG only
58
+ toast.appendChild(iconWrap);
59
+
60
+ const msg = document.createElement('span');
61
+ msg.className = 'cms-toast-msg';
62
+ msg.textContent = message; // never innerHTML — message may carry server text
63
+ toast.appendChild(msg);
64
+
65
+ if (actions && actions.length) {
66
+ const group = document.createElement('div');
67
+ group.className = 'cms-toast-actions';
68
+ for (const action of actions) {
69
+ const btn = document.createElement('button');
70
+ btn.type = 'button';
71
+ btn.className = `cms-toast-btn${action.primary ? ' cms-toast-btn-primary' : ''}`;
72
+ btn.textContent = action.label;
73
+ btn.addEventListener('click', () => {
74
+ dismiss(toast);
75
+ try {
76
+ action.onClick();
77
+ } catch (e) {
78
+ /* action errors shouldn't break the toast */
79
+ }
80
+ });
81
+ group.appendChild(btn);
82
+ }
83
+ toast.appendChild(group);
84
+ } else if (sticky) {
85
+ const close = document.createElement('button');
86
+ close.type = 'button';
87
+ close.className = 'cms-toast-close';
88
+ close.setAttribute('aria-label', 'Dismiss');
89
+ close.textContent = '×';
90
+ close.addEventListener('click', () => dismiss(toast));
91
+ toast.appendChild(close);
92
+ }
93
+
94
+ stack.appendChild(toast);
95
+ if (!sticky) setTimeout(() => dismiss(toast), 2500);
96
+ return toast;
97
+ }
98
+
99
+ function showToast(message, type) {
100
+ const t = type === 'error' ? 'error' : type || 'success';
101
+ return render(t, {
102
+ message,
103
+ sticky: t === 'error',
104
+ role: t === 'error' ? 'alert' : undefined,
105
+ });
106
+ }
107
+
108
+ // Two-choice save-conflict prompt. Sticky so the user's edit is never silently
109
+ // discarded — they explicitly choose to keep theirs or load the latest.
110
+ showToast.conflict = ({
111
+ message,
112
+ onKeepMine,
113
+ onLoadLatest,
114
+ keepLabel = 'Keep mine',
115
+ latestLabel = 'Load latest',
116
+ }) =>
117
+ render('warning', {
118
+ message,
119
+ sticky: true,
120
+ role: 'alert',
121
+ actions: [
122
+ { label: keepLabel, primary: true, onClick: onKeepMine },
123
+ { label: latestLabel, onClick: onLoadLatest },
124
+ ],
125
+ });
11
126
 
12
- const iconSvg =
13
- type === 'success'
14
- ? '<svg class="cms-toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>'
15
- : '<svg class="cms-toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>';
16
-
17
- toast.innerHTML = iconSvg + `<span>${message}</span>`;
18
- document.body.appendChild(toast);
19
- activeToast = toast;
20
-
21
- setTimeout(() => {
22
- toast.classList.add('cms-toast-out');
23
- setTimeout(() => {
24
- toast.remove();
25
- if (activeToast === toast) activeToast = null;
26
- }, 300);
27
- }, 2500);
28
- };
127
+ return showToast;
29
128
  }
@@ -16,14 +16,43 @@
16
16
  --cms-green: #22c55e;
17
17
  --cms-red: #ef4444;
18
18
  --cms-yellow: #eab308;
19
+ --cms-amber: #eab308;
19
20
  --cms-font: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
20
21
  }
21
22
 
23
+ /* ---------------------------------------------------------------------------
24
+ Host-style insulation
25
+
26
+ The editor chrome is injected as direct <body> children on the HOST site, so
27
+ an aggressive host reset (`* {}`, `body {}`, tag selectors) can bleed into it
28
+ — distorting fonts, line-height, letter-spacing, or box model. Pin the chrome
29
+ roots to sane defaults so they render consistently regardless of the host's
30
+ CSS. (Individual controls set their own type where needed.)
31
+ --------------------------------------------------------------------------- */
32
+ .cms-toolbar,
33
+ .cms-toast-stack,
34
+ .cms-toast,
35
+ .cms-link-popover,
36
+ .cms-content-map,
37
+ .cms-link-follow,
38
+ .cms-rich-toolbar,
39
+ .cms-studio-panel {
40
+ box-sizing: border-box;
41
+ font-family: var(--cms-font);
42
+ line-height: normal;
43
+ letter-spacing: normal;
44
+ text-transform: none;
45
+ text-align: left;
46
+ }
47
+
22
48
  /* ---------------------------------------------------------------------------
23
49
  Text editing affordances — simple blue outline
24
50
  --------------------------------------------------------------------------- */
25
51
 
26
- [data-caret].cms-editable {
52
+ /* Both field bindings (data-caret) and markdown body blocks (data-caret-md)
53
+ carry .cms-editable, so the hover/focus affordances apply to both. */
54
+ [data-caret].cms-editable,
55
+ [data-caret-md].cms-editable {
27
56
  cursor: text;
28
57
  transition: outline 0.2s, box-shadow 0.2s, background 0.4s;
29
58
  outline: 2px dashed transparent;
@@ -31,16 +60,48 @@
31
60
  border-radius: 2px;
32
61
  }
33
62
 
34
- [data-caret].cms-editable:hover {
63
+ [data-caret].cms-editable:hover,
64
+ [data-caret-md].cms-editable:hover {
35
65
  outline-color: var(--cms-blue-hover);
36
66
  }
37
67
 
38
- [data-caret].cms-editable:focus {
68
+ [data-caret].cms-editable:focus,
69
+ [data-caret-md].cms-editable:focus {
39
70
  outline: 2px solid var(--cms-blue);
40
71
  outline-offset: 4px;
41
72
  box-shadow: 0 0 0 4px var(--cms-blue-dim);
42
73
  }
43
74
 
75
+ /* ---------------------------------------------------------------------------
76
+ Unpublished body draft
77
+ The server rewrite tags blocks whose prose comes from an unpublished draft
78
+ (editor preview only — the public reads the base and never receives this).
79
+ Flag them so a reloaded preview is visibly distinct from live, published
80
+ prose and the editor knows a Publish is still pending.
81
+ --------------------------------------------------------------------------- */
82
+
83
+ [data-caret-md-draft] {
84
+ position: relative;
85
+ background: var(--cms-blue-dim);
86
+ box-shadow: -0.6em 0 0 0 var(--cms-blue-dim);
87
+ border-radius: 2px;
88
+ }
89
+
90
+ [data-caret-md-draft]::after {
91
+ content: 'Draft';
92
+ position: absolute;
93
+ top: -0.6em;
94
+ right: 0;
95
+ font: 600 10px / 1 var(--cms-font);
96
+ letter-spacing: 0.08em;
97
+ text-transform: uppercase;
98
+ color: #fff;
99
+ background: var(--cms-yellow);
100
+ padding: 3px 6px;
101
+ border-radius: 3px;
102
+ pointer-events: none;
103
+ }
104
+
44
105
  /* ---------------------------------------------------------------------------
45
106
  Link/button click prevention in editor mode
46
107
  --------------------------------------------------------------------------- */
@@ -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,13 +127,13 @@
88
127
  gap: 6px;
89
128
  padding: 6px 14px;
90
129
  font-family: var(--cms-font);
91
- font-size: 10px;
130
+ font-size: 11px;
92
131
  font-weight: 500;
93
132
  line-height: 1.2;
94
- letter-spacing: 0.12em;
133
+ letter-spacing: 0.08em;
95
134
  text-transform: uppercase;
96
135
  background: transparent;
97
- color: rgba(255, 255, 255, 0.5);
136
+ color: rgba(255, 255, 255, 0.72);
98
137
  border: 1px solid rgba(255, 255, 255, 0.1);
99
138
  border-radius: 2px;
100
139
  cursor: pointer;
@@ -150,7 +189,7 @@
150
189
  }
151
190
 
152
191
  .cms-status-text {
153
- color: rgba(255, 255, 255, 0.5);
192
+ color: rgba(255, 255, 255, 0.72);
154
193
  font-size: 11px;
155
194
  font-weight: 400;
156
195
  }
@@ -181,6 +220,17 @@
181
220
  display: none;
182
221
  }
183
222
 
223
+ /* Draft controls wrapper is transparent to the toolbar's flex layout so its
224
+ buttons keep the row's gap; [hidden] (server delivery, no pending draft)
225
+ still collapses the whole group. */
226
+ .cms-draft-controls {
227
+ display: contents;
228
+ }
229
+
230
+ .cms-draft-controls[hidden] {
231
+ display: none;
232
+ }
233
+
184
234
  /* Draft preview active — amber, signalling "unpublished" */
185
235
  .cms-toolbar button.cms-preview-btn-active {
186
236
  background: rgba(234, 179, 8, 0.12);
@@ -188,6 +238,19 @@
188
238
  color: var(--cms-yellow);
189
239
  }
190
240
 
241
+ /* Publish — primary action on static delivery */
242
+ .cms-toolbar button.cms-go-live-btn {
243
+ background: rgba(34, 197, 94, 0.12);
244
+ border-color: rgba(34, 197, 94, 0.3);
245
+ color: var(--cms-green);
246
+ }
247
+
248
+ .cms-toolbar button.cms-go-live-btn:hover {
249
+ background: rgba(34, 197, 94, 0.25);
250
+ border-color: rgba(34, 197, 94, 0.5);
251
+ color: var(--cms-green);
252
+ }
253
+
191
254
  /* Publish — green */
192
255
  .cms-toolbar button.cms-publish-btn {
193
256
  background: rgba(34, 197, 94, 0.12);
@@ -208,8 +271,9 @@
208
271
  color: var(--cms-red);
209
272
  }
210
273
 
211
- /* Highlight all mode — show every editable region */
212
- .cms-highlight-all [data-caret].cms-editable {
274
+ /* Highlight all mode — show every editable region (fields AND body blocks) */
275
+ .cms-highlight-all [data-caret].cms-editable,
276
+ .cms-highlight-all [data-caret-md].cms-editable {
213
277
  outline-color: var(--cms-blue-hover);
214
278
  background: rgba(59, 130, 246, 0.04);
215
279
  }
@@ -223,6 +287,7 @@
223
287
  transition — motion is suppressed, the information is not. */
224
288
  @media (prefers-reduced-motion: reduce) {
225
289
  .cms-highlight-all [data-caret].cms-editable,
290
+ .cms-highlight-all [data-caret-md].cms-editable,
226
291
  .cms-highlight-all img[data-caret] {
227
292
  transition: none;
228
293
  }
@@ -246,8 +311,11 @@
246
311
 
247
312
  .cms-nav-link {
248
313
  padding: 5px 14px;
249
- font-size: 10px;
250
- letter-spacing: 0.1em;
314
+ font-family: var(--cms-font);
315
+ font-size: 11px;
316
+ font-weight: 500;
317
+ line-height: 1.2;
318
+ letter-spacing: 0.08em;
251
319
  text-transform: uppercase;
252
320
  text-decoration: none;
253
321
  color: rgba(255, 255, 255, 0.35);