@goodfellow-cms/admin 0.4.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodfellow-cms/admin",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Goodfellow's admin panel: a visual editor for a site's pages, layout and settings.",
5
5
  "keywords": [
6
6
  "goodfellow",
@@ -31,6 +31,10 @@
31
31
  "types": "./dist/dev.d.ts",
32
32
  "import": "./dist/dev.js"
33
33
  },
34
+ "./editor": {
35
+ "types": "./dist/editor.d.ts",
36
+ "import": "./dist/editor.js"
37
+ },
34
38
  "./styles.css": "./styles.css",
35
39
  "./package.json": "./package.json"
36
40
  },
@@ -46,13 +50,14 @@
46
50
  "@codemirror/state": "6.7.6",
47
51
  "@codemirror/theme-one-dark": "6.1.3",
48
52
  "@codemirror/view": "6.43.14",
53
+ "@goodfellow-cms/ai": "0.5.0",
54
+ "@goodfellow-cms/core": "0.5.0",
55
+ "@goodfellow-cms/react": "0.5.0",
56
+ "@goodfellow-cms/registry": "0.5.0",
49
57
  "codemirror": "6.0.2",
58
+ "lucide-react": "1.52.0",
50
59
  "turndown": "7.2.4",
51
- "turndown-plugin-gfm": "1.0.2",
52
- "@goodfellow-cms/ai": "0.4.0",
53
- "@goodfellow-cms/react": "0.4.0",
54
- "@goodfellow-cms/registry": "0.4.0",
55
- "@goodfellow-cms/core": "0.4.0"
60
+ "turndown-plugin-gfm": "1.0.2"
56
61
  },
57
62
  "peerDependencies": {
58
63
  "@puckeditor/core": "0.23.0",
@@ -68,7 +73,7 @@
68
73
  "react-dom": "19.3.0"
69
74
  },
70
75
  "engines": {
71
- "node": ">=22"
76
+ "node": "^22.23.4 || ^24.22.1 || >=26.12.1"
72
77
  },
73
78
  "publishConfig": {
74
79
  "access": "public"
package/styles.css CHANGED
@@ -4,8 +4,11 @@
4
4
  */
5
5
  /* Each color is light-dark(light, dark): light-dark() picks by color-scheme, which follows
6
6
  * the computer's setting unless the admin panel's color menu chose (data-gfa-theme). */
7
- :root {
7
+ :root:not(.gfa-frame-render) {
8
8
  color-scheme: light dark;
9
+ }
10
+
11
+ :root {
9
12
  --gfa-bg: light-dark(#f8fafc, #0b1120);
10
13
  --gfa-surface: light-dark(#ffffff, #111827);
11
14
  --gfa-text: light-dark(#0f172a, #e2e8f0);
@@ -24,11 +27,18 @@
24
27
  --gfa-published-text: light-dark(#ffffff, #022c22);
25
28
  --gfa-error-bg: light-dark(#fef2f2, #3b0d0d);
26
29
  --gfa-error-text: light-dark(#991b1b, #fca5a5);
30
+ --gfa-warning-bg: light-dark(#fef3c7, #451a03);
31
+ --gfa-warning-text: light-dark(#92400e, #fde68a);
27
32
  --gfa-demo-bg: light-dark(#fef9c3, #422006);
28
33
  --gfa-demo-border: light-dark(#fde68a, #854d0e);
29
34
  --gfa-demo-text: light-dark(#713f12, #fde68a);
35
+ --gfa-draft-bg: light-dark(#eff6ff, #172554);
36
+ --gfa-draft-border: light-dark(#bfdbfe, #1e40af);
37
+ --gfa-draft-text: light-dark(#1e3a8a, #dbeafe);
30
38
  --gfa-checker: light-dark(#f1f5f9, #1e293b);
31
39
  --gfa-checker-alt: light-dark(#ffffff, #0f172a);
40
+ --gfa-tooltip-bg: light-dark(#0f172a, #e2e8f0);
41
+ --gfa-tooltip-text: light-dark(#f8fafc, #0b1120);
32
42
  --gfa-radius: 6px;
33
43
  --gfa-topbar-height: 52px;
34
44
  --gfa-code-active-line: light-dark(rgb(37 99 235 / 0.06), rgb(96 165 250 / 0.08));
@@ -98,6 +108,28 @@ body,
98
108
  font-size: 14px;
99
109
  }
100
110
 
111
+ .gfa-draft-banner {
112
+ display: flex;
113
+ flex-wrap: wrap;
114
+ align-items: center;
115
+ justify-content: center;
116
+ gap: 8px 12px;
117
+ flex-shrink: 0;
118
+ padding: 6px 16px;
119
+ background: var(--gfa-draft-bg);
120
+ border-bottom: 1px solid var(--gfa-draft-border);
121
+ color: var(--gfa-draft-text);
122
+ font-size: 14px;
123
+ }
124
+
125
+ .gfa-draft-banner .gfa-draft-count {
126
+ font-weight: 600;
127
+ }
128
+
129
+ .gfa-draft-banner a {
130
+ color: inherit;
131
+ }
132
+
101
133
  .gfa-brand {
102
134
  font-weight: 700;
103
135
  white-space: nowrap;
@@ -151,6 +183,154 @@ body,
151
183
  overflow: auto;
152
184
  }
153
185
 
186
+ /* Site Settings: the sidebar, and the tab chosen beside it, left-aligned. */
187
+ .gfa-settings {
188
+ display: flex;
189
+ height: 100%;
190
+ min-height: 0;
191
+ }
192
+
193
+ .gfa-settings-content {
194
+ flex: 1;
195
+ min-width: 0;
196
+ overflow: auto;
197
+ }
198
+
199
+ .gfa-settings-content > .gfa-screen {
200
+ margin: 0;
201
+ padding: 24px;
202
+ }
203
+
204
+ .gfa-sidebar {
205
+ display: flex;
206
+ flex-direction: column;
207
+ flex-shrink: 0;
208
+ width: 216px;
209
+ padding: 12px 8px;
210
+ box-sizing: border-box;
211
+ background: var(--gfa-surface);
212
+ border-right: 1px solid var(--gfa-border);
213
+ overflow-y: auto;
214
+ transition: width 180ms ease;
215
+ }
216
+
217
+ .gfa-sidebar[data-collapsed] {
218
+ width: 56px;
219
+ /* Tooltips hang outside it. */
220
+ overflow: visible;
221
+ }
222
+
223
+ .gfa-sidebar-list {
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: 2px;
227
+ margin: 0;
228
+ padding: 0;
229
+ list-style: none;
230
+ }
231
+
232
+ .gfa-sidebar-divider {
233
+ width: 100%;
234
+ margin: 10px 0;
235
+ border: 0;
236
+ border-top: 1px solid var(--gfa-border);
237
+ }
238
+
239
+ .gfa-sidebar-item {
240
+ position: relative;
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 10px;
244
+ width: 100%;
245
+ min-height: 36px;
246
+ padding: 0 10px;
247
+ box-sizing: border-box;
248
+ border: 0;
249
+ border-radius: var(--gfa-radius);
250
+ background: none;
251
+ color: var(--gfa-muted);
252
+ font: inherit;
253
+ font-size: 14px;
254
+ text-align: left;
255
+ text-decoration: none;
256
+ white-space: nowrap;
257
+ cursor: pointer;
258
+ }
259
+
260
+ .gfa-sidebar-item:hover {
261
+ background: var(--gfa-bg);
262
+ color: var(--gfa-text);
263
+ }
264
+
265
+ .gfa-sidebar-item[aria-current="page"] {
266
+ background: var(--gfa-current-bg);
267
+ color: var(--gfa-accent);
268
+ font-weight: 600;
269
+ }
270
+
271
+ .gfa-sidebar-item:focus-visible {
272
+ outline: 2px solid var(--gfa-accent);
273
+ outline-offset: -2px;
274
+ }
275
+
276
+ .gfa-sidebar-icon {
277
+ flex-shrink: 0;
278
+ }
279
+
280
+ .gfa-sidebar-label {
281
+ overflow: hidden;
282
+ text-overflow: ellipsis;
283
+ }
284
+
285
+ .gfa-sidebar-toggle {
286
+ margin-top: auto;
287
+ }
288
+
289
+ /* Collapsed, each name becomes a tooltip beside its icon, which fades and slides in. */
290
+ .gfa-sidebar[data-collapsed] .gfa-sidebar-label {
291
+ position: absolute;
292
+ left: calc(100% + 10px);
293
+ top: 50%;
294
+ z-index: 20;
295
+ padding: 5px 9px;
296
+ border-radius: var(--gfa-radius);
297
+ background: var(--gfa-tooltip-bg);
298
+ color: var(--gfa-tooltip-text);
299
+ font-size: 13px;
300
+ font-weight: 500;
301
+ box-shadow: 0 4px 12px rgb(15 23 42 / 0.18);
302
+ pointer-events: none;
303
+ opacity: 0;
304
+ transform: translate(-6px, -50%);
305
+ transition:
306
+ opacity 140ms ease,
307
+ transform 140ms ease;
308
+ }
309
+
310
+ .gfa-sidebar[data-collapsed] .gfa-sidebar-label::before {
311
+ content: "";
312
+ position: absolute;
313
+ right: 100%;
314
+ top: 50%;
315
+ border: 5px solid transparent;
316
+ border-right-color: var(--gfa-tooltip-bg);
317
+ transform: translateY(-50%);
318
+ }
319
+
320
+ .gfa-sidebar[data-collapsed] .gfa-sidebar-item:hover .gfa-sidebar-label,
321
+ .gfa-sidebar[data-collapsed] .gfa-sidebar-item:focus-visible .gfa-sidebar-label {
322
+ opacity: 1;
323
+ transform: translate(0, -50%);
324
+ transition-delay: 120ms;
325
+ }
326
+
327
+ @media (prefers-reduced-motion: reduce) {
328
+ .gfa-sidebar,
329
+ .gfa-sidebar[data-collapsed] .gfa-sidebar-label {
330
+ transition: none;
331
+ }
332
+ }
333
+
154
334
  .gfa-screen {
155
335
  max-width: 960px;
156
336
  margin: 0 auto;
@@ -418,6 +598,66 @@ body,
418
598
  color: var(--gfa-error-text);
419
599
  }
420
600
 
601
+ .gfa-notice-warning {
602
+ background: var(--gfa-demo-bg);
603
+ border-color: var(--gfa-demo-border);
604
+ color: var(--gfa-demo-text);
605
+ }
606
+
607
+ /* Scheduled publishing: when a page or item is on the site, and what's coming up. */
608
+ .gfa-schedule {
609
+ margin: 0;
610
+ padding: 0;
611
+ border: 0;
612
+ min-width: 0;
613
+ }
614
+
615
+ .gfa-schedule-times {
616
+ display: flex;
617
+ flex-wrap: wrap;
618
+ gap: 0 16px;
619
+ }
620
+
621
+ .gfa-schedule-times .gfa-field {
622
+ flex: 1 1 14rem;
623
+ }
624
+
625
+ .gfa-schedule-status {
626
+ display: block;
627
+ margin-top: 2px;
628
+ color: var(--gfa-muted);
629
+ font-size: 13px;
630
+ }
631
+
632
+ .gfa-coming-up {
633
+ margin: 0 0 24px;
634
+ padding: 12px 16px;
635
+ border: 1px solid var(--gfa-border);
636
+ border-radius: var(--gfa-radius);
637
+ background: var(--gfa-surface);
638
+ }
639
+
640
+ .gfa-coming-up ul {
641
+ margin: 0 0 8px;
642
+ padding: 0;
643
+ list-style: none;
644
+ }
645
+
646
+ .gfa-coming-up li {
647
+ display: flex;
648
+ flex-wrap: wrap;
649
+ gap: 0 8px;
650
+ padding: 4px 0;
651
+ }
652
+
653
+ .gfa-coming-up time {
654
+ font-variant-numeric: tabular-nums;
655
+ }
656
+
657
+ .gfa-coming-up-kind {
658
+ color: var(--gfa-muted);
659
+ }
660
+
421
661
  .gfa-details summary {
422
662
  cursor: pointer;
423
663
  margin-bottom: 8px;
@@ -460,10 +700,20 @@ body,
460
700
  margin-top: 8px;
461
701
  }
462
702
 
703
+ .gfa-check-list {
704
+ display: grid;
705
+ gap: 6px;
706
+ max-height: 40vh;
707
+ margin: 0 0 16px;
708
+ padding-left: 20px;
709
+ overflow-y: auto;
710
+ }
711
+
463
712
  .gfa-editor {
464
713
  position: relative;
465
714
  display: flex;
466
715
  flex-direction: column;
716
+ width: 100%;
467
717
  height: 100%;
468
718
  }
469
719
 
@@ -487,13 +737,33 @@ body,
487
737
  height: 100%;
488
738
  }
489
739
 
490
- .gfa-layout-screen > .gfa-tabs {
491
- margin: 0;
740
+ .gfa-layout-bar {
741
+ display: flex;
742
+ flex-wrap: wrap;
743
+ align-items: center;
744
+ justify-content: space-between;
745
+ gap: 8px 16px;
492
746
  padding: 8px 16px;
493
747
  background: var(--gfa-surface);
494
748
  border-bottom: 1px solid var(--gfa-border);
495
749
  }
496
750
 
751
+ .gfa-layout-bar > .gfa-tabs {
752
+ margin: 0;
753
+ }
754
+
755
+ /* A field with its label beside it, such as the layout editor's language. */
756
+ .gfa-inline-field {
757
+ display: inline-flex;
758
+ align-items: center;
759
+ gap: 8px;
760
+ font-size: 14px;
761
+ }
762
+
763
+ .gfa-inline-field > .gfa-input {
764
+ width: auto;
765
+ }
766
+
497
767
  .gfa-layout-screen > .gfa-editor {
498
768
  flex: 1;
499
769
  min-height: 0;
@@ -725,6 +995,98 @@ body,
725
995
  min-height: 0;
726
996
  }
727
997
 
998
+ /* The editor's header, outside the frame that runs the site's code. */
999
+ .gfa-editor-header {
1000
+ display: flex;
1001
+ flex-wrap: wrap;
1002
+ align-items: center;
1003
+ gap: 8px 16px;
1004
+ padding: 8px 16px;
1005
+ background: var(--gfa-surface);
1006
+ border-bottom: 1px solid var(--gfa-border);
1007
+ }
1008
+
1009
+ .gfa-editor-title {
1010
+ flex: 1;
1011
+ min-width: 0;
1012
+ margin: 0;
1013
+ overflow: hidden;
1014
+ font-size: 15px;
1015
+ white-space: nowrap;
1016
+ text-overflow: ellipsis;
1017
+ }
1018
+
1019
+ .gfa-editor-title code {
1020
+ color: var(--gfa-muted);
1021
+ font-size: 13px;
1022
+ font-weight: 400;
1023
+ }
1024
+
1025
+ .gfa-editor-tools {
1026
+ display: flex;
1027
+ align-items: center;
1028
+ gap: 4px;
1029
+ }
1030
+
1031
+ .gfa-editor-tools .gfa-button[aria-pressed="true"],
1032
+ .gfa-header-actions .gfa-button[aria-pressed="true"] {
1033
+ background: var(--gfa-current-bg);
1034
+ }
1035
+
1036
+ .gfa-icon-button {
1037
+ display: inline-flex;
1038
+ align-items: center;
1039
+ justify-content: center;
1040
+ padding: 6px;
1041
+ }
1042
+
1043
+ .gfa-editor-body {
1044
+ display: flex;
1045
+ flex: 1;
1046
+ min-height: 0;
1047
+ }
1048
+
1049
+ .gfa-editor-ai-panel {
1050
+ flex: none;
1051
+ width: min(360px, 40vw);
1052
+ overflow-y: auto;
1053
+ background: var(--gfa-surface);
1054
+ border-right: 1px solid var(--gfa-border);
1055
+ }
1056
+
1057
+ .gfa-editor-frame-wrap {
1058
+ flex: 1;
1059
+ min-width: 0;
1060
+ display: flex;
1061
+ }
1062
+
1063
+ .gfa-editor-frame {
1064
+ flex: 1;
1065
+ width: 100%;
1066
+ height: 100%;
1067
+ border: 0;
1068
+ background: var(--gfa-bg);
1069
+ }
1070
+
1071
+ /* The frames that run the site's code: the editor fills its page, and its canvas can be a phone's width. */
1072
+ :root.gfa-frame,
1073
+ .gfa-frame body {
1074
+ height: 100%;
1075
+ margin: 0;
1076
+ }
1077
+
1078
+ .gfa-frame-editor #goodfellow-frame,
1079
+ .gfa-frame-editor .gfa-editor-puck {
1080
+ height: 100%;
1081
+ }
1082
+
1083
+ .gfa-frame-editor .gf-canvas {
1084
+ width: var(--gf-canvas-width, auto);
1085
+ max-width: 100%;
1086
+ min-height: 100%;
1087
+ margin-inline: auto;
1088
+ }
1089
+
728
1090
  .gfa-editor-notice {
729
1091
  padding: 8px 16px;
730
1092
  background: var(--gfa-surface);
@@ -1134,11 +1496,9 @@ body,
1134
1496
 
1135
1497
  /*
1136
1498
  * Items have no blocks, so the item editor leaves out Puck's Blocks and Outline
1137
- * buttons, always the first two in its side bar (after the legacy side bar's,
1138
- * which only shows on wide screens, when the AI assistant is off).
1499
+ * buttons, the two after the legacy side bar's, which only shows on wide screens.
1139
1500
  */
1140
- .gfa-editor-entry.gfa-editor-ai nav[class*="_Nav_"] > ul > li:nth-child(-n + 2),
1141
- .gfa-editor-entry:not(.gfa-editor-ai) nav[class*="_Nav_"] > ul > li:nth-child(n + 2):nth-child(-n + 3) {
1501
+ .gfa-editor-for-entry nav[class*="_Nav_"] > ul > li:nth-child(n + 2):nth-child(-n + 3) {
1142
1502
  display: none;
1143
1503
  }
1144
1504
 
@@ -1337,6 +1697,15 @@ body,
1337
1697
  overflow: hidden;
1338
1698
  }
1339
1699
 
1700
+ .gfa-site-changes {
1701
+ display: grid;
1702
+ gap: 8px;
1703
+ }
1704
+
1705
+ .gfa-site-changes .gfa-label {
1706
+ margin: 0 0 4px;
1707
+ }
1708
+
1340
1709
  @media (max-width: 900px) {
1341
1710
  .gfa-versions-layout {
1342
1711
  grid-template-columns: 1fr;
@@ -1484,3 +1853,137 @@ body,
1484
1853
  justify-content: space-between;
1485
1854
  gap: 8px;
1486
1855
  }
1856
+
1857
+ /* How a link to a page looks when it's shared, at the end of the page's settings. */
1858
+ .gfa-share-preview {
1859
+ padding: 16px;
1860
+ border-top: 1px solid var(--gfa-border);
1861
+ }
1862
+
1863
+ .gfa-share-card {
1864
+ overflow: hidden;
1865
+ margin: 8px 0;
1866
+ border: 1px solid var(--gfa-border);
1867
+ border-radius: var(--gfa-radius);
1868
+ background: var(--gfa-surface);
1869
+ }
1870
+
1871
+ .gfa-share-image {
1872
+ display: block;
1873
+ width: 100%;
1874
+ aspect-ratio: 1.91 / 1;
1875
+ object-fit: cover;
1876
+ background: var(--gfa-checker);
1877
+ }
1878
+
1879
+ .gfa-share-no-image {
1880
+ display: grid;
1881
+ place-items: center;
1882
+ color: var(--gfa-muted);
1883
+ font-size: 13px;
1884
+ }
1885
+
1886
+ .gfa-share-text {
1887
+ display: grid;
1888
+ gap: 2px;
1889
+ padding: 8px 12px;
1890
+ font-size: 14px;
1891
+ }
1892
+
1893
+ .gfa-share-host {
1894
+ color: var(--gfa-muted);
1895
+ font-size: 12px;
1896
+ }
1897
+
1898
+ .gfa-share-description {
1899
+ display: -webkit-box;
1900
+ overflow: hidden;
1901
+ color: var(--gfa-muted);
1902
+ -webkit-box-orient: vertical;
1903
+ -webkit-line-clamp: 2;
1904
+ line-clamp: 2;
1905
+ }
1906
+
1907
+ /* Import from WordPress */
1908
+ .gfa-import-address {
1909
+ display: flex;
1910
+ flex-wrap: wrap;
1911
+ gap: 8px;
1912
+ }
1913
+
1914
+ .gfa-import-address .gfa-input {
1915
+ flex: 1 1 288px;
1916
+ width: auto;
1917
+ }
1918
+
1919
+ .gfa-import-summary,
1920
+ .gfa-import-notes {
1921
+ margin: 0;
1922
+ padding-left: 20px;
1923
+ line-height: 1.6;
1924
+ }
1925
+
1926
+ .gfa-import-notes li + li {
1927
+ margin-top: 6px;
1928
+ }
1929
+
1930
+ .gfa-import-actions {
1931
+ display: flex;
1932
+ flex-wrap: wrap;
1933
+ gap: 8px;
1934
+ }
1935
+
1936
+ /* One of the site's other languages, on Site Settings' Languages tab. */
1937
+ .gfa-language-card {
1938
+ display: flex;
1939
+ flex-direction: column;
1940
+ gap: 12px;
1941
+ padding: 16px;
1942
+ border: 1px solid var(--gfa-border);
1943
+ border-radius: var(--gfa-radius);
1944
+ background: var(--gfa-surface);
1945
+ }
1946
+
1947
+ .gfa-language-card > .gfa-section-title {
1948
+ margin: 0;
1949
+ }
1950
+
1951
+ .gfa-language-card > .gfa-button {
1952
+ align-self: flex-start;
1953
+ }
1954
+
1955
+ /* A page's or item's translations, in the Pages screen's and collections' lists. */
1956
+ .gfa-translations {
1957
+ display: flex;
1958
+ flex-wrap: wrap;
1959
+ align-items: center;
1960
+ gap: 4px 12px;
1961
+ margin-top: 4px;
1962
+ font-size: 14px;
1963
+ }
1964
+
1965
+ .gfa-translation {
1966
+ display: inline-flex;
1967
+ align-items: center;
1968
+ gap: 6px;
1969
+ }
1970
+
1971
+ .gfa-badge-warning {
1972
+ background: var(--gfa-warning-bg);
1973
+ color: var(--gfa-warning-text);
1974
+ }
1975
+
1976
+ .gfa-ai-translate {
1977
+ display: flex;
1978
+ flex-direction: column;
1979
+ align-items: flex-start;
1980
+ gap: 8px;
1981
+ padding-bottom: 12px;
1982
+ border-bottom: 1px solid var(--gfa-border);
1983
+ }
1984
+
1985
+ .gfa-editor-viewports {
1986
+ margin: 0;
1987
+ padding: 0;
1988
+ border: 0;
1989
+ }