smoodly 0.0.61 → 0.0.62
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/dist/admin/editor/CanvasOverlay.d.ts +4 -1
- package/dist/admin/editor/CanvasOverlay.js +3 -3
- package/dist/admin/editor/GlobalCanvas.d.ts +27 -0
- package/dist/admin/editor/GlobalCanvas.js +159 -0
- package/dist/admin/editor/InsertPicker.js +1 -1
- package/dist/admin/files.generated.js +1 -1
- package/dist/admin/serialize.d.ts +2 -0
- package/dist/admin/serialize.js +1 -0
- package/dist/admin/shell/AdminApp.js +5 -5
- package/dist/admin/shell/GlobalForm.js +80 -7
- package/dist/admin/shell/GlobalList.js +1 -1
- package/dist/admin/shell/{IconRail.d.ts → NavBar.d.ts} +3 -3
- package/dist/admin/shell/NavBar.js +120 -0
- package/dist/admin/shell/global-canvas.d.ts +40 -0
- package/dist/admin/shell/global-canvas.js +60 -0
- package/dist/admin/shell/health-list.js +1 -1
- package/dist/admin/ui/primitives.js +1 -1
- package/dist/admin/ui/theme.js +99 -91
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/collections/defining.md +2 -2
- package/docs/content-modeling/zones.md +2 -2
- package/docs/getting-started/deploying.md +1 -1
- package/docs/getting-started/working-together.md +1 -1
- package/docs/globals/defining.md +1 -1
- package/docs/globals/header-and-footer.md +2 -2
- package/docs/media/library.md +1 -1
- package/package.json +1 -1
- package/dist/admin/shell/IconRail.js +0 -109
package/dist/admin/ui/theme.js
CHANGED
|
@@ -34,8 +34,8 @@ const TOKENS_LIGHT = `
|
|
|
34
34
|
--selection-global:#C4620C;
|
|
35
35
|
--hover:rgba(21,23,39,.05); --press:rgba(21,23,39,.09); --control-hover:#ECEDF1; --canvas-ground:#EDEDED;
|
|
36
36
|
--btn-secondary-bg:var(--surface); --btn-secondary-hover:#ECEDF1;
|
|
37
|
-
/* The
|
|
38
|
-
--
|
|
37
|
+
/* The navigation bar sits on the frame: its own greys, the active item a white tint, its dark menus a step lighter than the frame. */
|
|
38
|
+
--navbar-fg:#B9BDCE; --navbar-active-fg:#FFFFFF; --navbar-active-bg:rgba(255,255,255,.14); --navbar-hover-bg:rgba(255,255,255,.08); --navbar-menu-bg:#242840;
|
|
39
39
|
--bar-bg:transparent; --bar-border:0; --menu-border:0; --card-divider:0;
|
|
40
40
|
--card-title-size:var(--text-md); --card-title-color:var(--text);
|
|
41
41
|
/* In-card segmented (tabs, the span bar): white track, the chosen option in the tile grey. */
|
|
@@ -52,7 +52,7 @@ const TOKENS_LIGHT = `
|
|
|
52
52
|
dark mode that washed out over the white page, 2026-09-10). */
|
|
53
53
|
--plus-bg:#DDEBE4; --plus-hover:#D0E3D9; --plus-fg:#1C6E5B;
|
|
54
54
|
--plus-icon-bg:#1C6E5B; --plus-icon-fg:#FFFFFF; --plus-shadow:none;
|
|
55
|
-
--logo-color:var(--accent); --logo-outer:#0B5A49; --logo-inner:#5FC9A0; /* the
|
|
55
|
+
--logo-color:var(--accent); --logo-outer:#0B5A49; --logo-inner:#5FC9A0; /* the bar's app icon: deep green square, mint s (2026-10-23) */
|
|
56
56
|
|
|
57
57
|
--status-published-fg:var(--green-700); --status-published-bg:var(--green-50);
|
|
58
58
|
--status-changes-fg:var(--amber-700); --status-changes-bg:var(--amber-50);
|
|
@@ -78,7 +78,7 @@ const TOKENS_LIGHT = `
|
|
|
78
78
|
`;
|
|
79
79
|
/* The handoff's dark proposal (VisualEditor.md, 8c right board): the frame
|
|
80
80
|
and sheet a step darker, tiles a step lighter, fields in the sheet tone;
|
|
81
|
-
the
|
|
81
|
+
the bar and the logo unchanged. */
|
|
82
82
|
const TOKENS_DARK = `
|
|
83
83
|
--accent:#6CC4A6; --accent-hover:#82D2B8; --accent-soft:rgba(108,196,166,.18); --accent-line:#6CC4A6; --on-accent:#151727;
|
|
84
84
|
--selection-global:#F09A5C;
|
|
@@ -133,10 +133,10 @@ const SCALE = `
|
|
|
133
133
|
--control-h:40px; --control-h-sm:34px; --control-h-lg:48px; --button-h:36px; --button-h-sm:28px; --row-h:48px; --topbar-h:64px;
|
|
134
134
|
--outline-row-h:36px; --seg-pad-y:7px; --tabs-pad:3px;
|
|
135
135
|
--plus-h:32px; --plus-pad:0 14px 0 6px; --plus-label:inline;
|
|
136
|
-
--
|
|
137
|
-
/* The frame is 8px
|
|
138
|
-
--frame-pad:8px; --
|
|
139
|
-
--
|
|
136
|
+
--inspector-w:360px; --gutter:20px;
|
|
137
|
+
/* The frame is 8px left of, right of and below the sheet (6px in the handoff, +2px 2026-10-23); the bar fills the top edge. */
|
|
138
|
+
--frame-pad:8px; --shell-gap:0px; --sheet-radius:20px; --sheet-pad:0 24px 24px;
|
|
139
|
+
--navbar-h:64px; --navbar-pad-x:12px; --navbar-item-h:40px; --navbar-item-radius:10px;
|
|
140
140
|
--topbar-pad:0; --back-size:32px; --back-bg:var(--surface);
|
|
141
141
|
--table-pad:8px 12px; --table-top:80px; --canvas-pad:28px 40px 0; --card-pad:16px 18px; --card-gap:12px; --form-pad:32px 40px;
|
|
142
142
|
--border:1px solid var(--line);
|
|
@@ -182,106 +182,106 @@ html:has(.sm-admin),body:has(.sm-admin){overscroll-behavior:none}
|
|
|
182
182
|
.sm-code{background:var(--surface);border-radius:8px;padding:10px 12px;margin:0;font:13px/1.5 var(--font-mono);overflow:auto}
|
|
183
183
|
|
|
184
184
|
/* ── shell ─────────────────────────────────────────────────────────── */
|
|
185
|
-
/*
|
|
186
|
-
components/AppShell.html): navy,
|
|
185
|
+
/* Bar over main (2026-11-14; a 72px rail on the left before it). The shell is
|
|
186
|
+
the frame (design_handoff_smoodly_neutral/components/AppShell.html): navy,
|
|
187
|
+
the navigation bar across its top, 8px right of, left of and below the main
|
|
187
188
|
column, which is the white sheet with a 20px radius and its own inner
|
|
188
|
-
padding
|
|
189
|
+
padding. The shell owns the viewport and the sheet scrolls inside itself,
|
|
190
|
+
so the bar stays put.
|
|
189
191
|
The main column stacks the top bar over one tile-shaped region: list,
|
|
190
192
|
form or editor. */
|
|
191
193
|
.sm-shell{
|
|
192
|
-
display:grid;grid-template-
|
|
193
|
-
padding:
|
|
194
|
+
display:grid;grid-template-rows:var(--navbar-h) minmax(0,1fr);grid-template-columns:minmax(0,1fr);row-gap:var(--shell-gap);
|
|
195
|
+
padding:0 var(--frame-pad) var(--frame-pad);height:100vh;height:100dvh;overflow:hidden;background:var(--frame);
|
|
194
196
|
}
|
|
195
197
|
.sm-shell>.sm-main{
|
|
196
198
|
min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;
|
|
197
199
|
background:var(--sheet);border-radius:var(--sheet-radius);padding:var(--sheet-pad);
|
|
198
200
|
}
|
|
199
201
|
.sm-wordmark{height:18px;width:auto;color:var(--logo-color);filter:var(--logo-filter);flex:none}
|
|
200
|
-
/* The
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
202
|
+
/* The navigation bar: the app icon on the left, the items centred, Deploy and
|
|
203
|
+
the avatar on the right. The rail's colours, icons and order: 18px Lucide
|
|
204
|
+
icons beside a 14px label, the active item a white-tint pill. */
|
|
205
|
+
.sm-navbar{
|
|
206
|
+
display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:16px;min-width:0;
|
|
207
|
+
padding:0 var(--navbar-pad-x);background:var(--bar-bg);color:var(--navbar-fg);
|
|
208
|
+
font:500 var(--text-md)/1 var(--font-sans);position:relative;z-index:30;
|
|
209
|
+
}
|
|
210
|
+
.sm-navbar__start{display:flex;align-items:center;justify-self:start}
|
|
211
|
+
.sm-navbar__end{display:flex;align-items:center;gap:10px;justify-self:end}
|
|
209
212
|
/* The app icon (the Smoodly menu's trigger): the deep green square with the
|
|
210
|
-
mint s
|
|
211
|
-
|
|
212
|
-
.sm-rail__brand{appearance:none;background:none;border:0;padding:0;margin-bottom:6px;cursor:pointer;display:block;border-radius:6px;color:inherit}
|
|
213
|
+
mint s (2026-10-23). */
|
|
214
|
+
.sm-navbar__brand{appearance:none;background:none;border:0;padding:0;cursor:pointer;display:block;border-radius:8px;color:inherit}
|
|
213
215
|
/* Hover (2026-10-23): the app icon and the avatar brighten a touch. */
|
|
214
|
-
.sm-
|
|
215
|
-
.sm-
|
|
216
|
-
.sm-
|
|
217
|
-
.sm-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
.sm-
|
|
222
|
-
.sm-
|
|
223
|
-
.sm-
|
|
224
|
-
.sm-
|
|
225
|
-
.sm-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
.sm-
|
|
233
|
-
.sm-
|
|
234
|
-
.sm-
|
|
235
|
-
.sm-
|
|
236
|
-
.sm-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
.sm-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
.sm-
|
|
250
|
-
.sm-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
.sm-
|
|
254
|
-
.sm-
|
|
255
|
-
.sm-
|
|
256
|
-
.
|
|
257
|
-
.sm-rail__foot{margin-top:auto;display:flex;flex-direction:column;align-items:center}
|
|
258
|
-
.sm-rail__user{
|
|
259
|
-
width:28px;height:28px;border-radius:var(--radius-round);background:var(--accent-soft);color:var(--accent);
|
|
260
|
-
display:grid;place-items:center;font-size:var(--text-xs);font-weight:600;border:0;cursor:pointer;padding:0;
|
|
261
|
-
}
|
|
262
|
-
.sm-rail__user:focus-visible{outline:2px solid var(--rail-active-fg);outline-offset:1px;box-shadow:none}
|
|
263
|
-
/* Flyout: 6px right of the rail, aligned with its item (the user menu aligns to the bottom).
|
|
216
|
+
.sm-navbar__logo{width:30px;height:auto;display:block;flex:none;transition:filter .15s}
|
|
217
|
+
.sm-navbar__brand:hover .sm-navbar__logo{filter:brightness(1.3)}
|
|
218
|
+
.sm-navbar__brand:focus-visible{outline:2px solid var(--navbar-active-fg);outline-offset:2px;box-shadow:none}
|
|
219
|
+
.sm-navbar__user{
|
|
220
|
+
width:32px;height:32px;border-radius:var(--radius-round);background:var(--accent-soft);color:var(--accent);
|
|
221
|
+
display:grid;place-items:center;font-size:var(--text-xs);font-weight:600;border:0;cursor:pointer;padding:0;transition:background .15s;
|
|
222
|
+
}
|
|
223
|
+
.sm-navbar__user:hover{background:color-mix(in srgb,var(--accent-soft),#fff 60%)}
|
|
224
|
+
.sm-navbar__user:focus-visible{outline:2px solid var(--navbar-active-fg);outline-offset:2px;box-shadow:none}
|
|
225
|
+
.sm-navbar__ext{width:13px;height:13px;flex:none;margin-left:12px;opacity:.45}
|
|
226
|
+
.sm-navbar__menu-item:hover .sm-navbar__ext,.sm-navbar__menu-item:focus-visible .sm-navbar__ext{opacity:.8}
|
|
227
|
+
.sm-navbar__menu-version{font-size:var(--text-xs);color:var(--text-subtle);padding:7px 10px 4px;margin-top:3px;border-top:1px solid var(--line)}
|
|
228
|
+
.sm-navbar__list{display:flex;align-items:center;gap:4px;margin:0;padding:0;list-style:none;min-width:0}
|
|
229
|
+
.sm-navbar__item{position:relative;list-style:none}
|
|
230
|
+
.sm-navbar__btn{
|
|
231
|
+
appearance:none;background:none;border:0;margin:0;height:var(--navbar-item-h);padding:0 12px;display:flex;align-items:center;gap:8px;
|
|
232
|
+
color:inherit;font:inherit;cursor:pointer;border-radius:var(--navbar-item-radius);text-decoration:none;white-space:nowrap;transition:background .12s,color .12s;
|
|
233
|
+
}
|
|
234
|
+
.sm-navbar__btn--icon{padding:0;width:var(--navbar-item-h);justify-content:center}
|
|
235
|
+
.sm-navbar__btn:hover{color:var(--navbar-active-fg);background:var(--navbar-hover-bg)}
|
|
236
|
+
.sm-navbar__btn:focus-visible{outline:2px solid var(--navbar-active-fg);outline-offset:-2px;box-shadow:none}
|
|
237
|
+
.sm-navbar__btn[aria-current="page"],.sm-navbar__btn[aria-expanded="true"]{color:var(--navbar-active-fg);background:var(--navbar-active-bg)}
|
|
238
|
+
.sm-navbar__btn svg{flex:none;stroke:currentColor;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
239
|
+
.sm-navbar__chevron{margin-left:-2px;opacity:.8}
|
|
240
|
+
/* A collection's own title can be long: it ends in an ellipsis (2026-11-13). */
|
|
241
|
+
.sm-navbar__label{letter-spacing:.01em;white-space:nowrap;max-width:160px;overflow:hidden;text-overflow:ellipsis}
|
|
242
|
+
/* A narrow window keeps the icons and drops the labels; each item keeps its name for a screen reader and a tooltip. */
|
|
243
|
+
@media (max-width:820px){
|
|
244
|
+
.sm-navbar__list .sm-navbar__label,.sm-navbar__deploy .sm-navbar__label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
|
|
245
|
+
.sm-navbar__btn{padding:0 10px}
|
|
246
|
+
}
|
|
247
|
+
/* The Deploy item (spec 2026-10-09 static mode §6): next to the avatar; the badge is the count waiting, the
|
|
248
|
+
lit item a build in flight, the warning item a build that has not landed or a site that did not answer. */
|
|
249
|
+
.sm-navbar__icon{position:relative;display:grid;place-items:center}
|
|
250
|
+
.sm-navbar__badge{position:absolute;top:-7px;right:-9px;min-width:16px;height:16px;padding:0 4px;border-radius:var(--radius-round);background:var(--accent);color:#fff;font-size:10px;font-weight:600;line-height:16px;text-align:center}
|
|
251
|
+
.sm-navbar__btn--lit,.sm-navbar__btn--lit:hover{background:var(--accent-soft);color:var(--accent);animation:sm-navbar-pulse 1.6s ease-in-out infinite}
|
|
252
|
+
.sm-navbar__btn--warn,.sm-navbar__btn--warn:hover{background:var(--warn-soft);color:var(--warn)}
|
|
253
|
+
@keyframes sm-navbar-pulse{0%,100%{opacity:1}50%{opacity:.55}}
|
|
254
|
+
.sm-navbar__menu-detail{font-size:var(--text-sm);color:var(--text);padding:6px 8px 4px;white-space:normal}
|
|
255
|
+
.sm-navbar__menu-note{font-size:var(--text-xs);color:var(--text-muted);padding:4px 8px 6px;white-space:normal}
|
|
256
|
+
.sm-navbar__menu-item:disabled{cursor:default;color:var(--text-muted)}
|
|
257
|
+
.sm-navbar__menu-item:disabled:hover{background:none}
|
|
258
|
+
/* A menu drops 6px below its item, aligned with its left edge (the end group's with its right edge).
|
|
264
259
|
Menus float on --menu-bg (the sheet white in the neutral look, so they read over the frame). */
|
|
265
|
-
.sm-
|
|
266
|
-
position:absolute;left:calc(100% + 6px);
|
|
260
|
+
.sm-navbar__menu{
|
|
261
|
+
position:absolute;left:0;top:calc(100% + 6px);z-index:20;width:200px;padding:6px;margin:0;list-style:none;
|
|
267
262
|
background:var(--menu-bg);border:var(--menu-border);border-radius:var(--radius-md);box-shadow:var(--shadow-pop);
|
|
268
|
-
display:none;flex-direction:column;gap:1px;font-size:var(--text-md);font-weight:500;color:var(--text);
|
|
269
|
-
}
|
|
270
|
-
.sm-
|
|
271
|
-
.sm-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
.sm-
|
|
275
|
-
.sm-
|
|
276
|
-
.sm-
|
|
277
|
-
.sm-rail__menu--collections .sm-rail__menu-item{padding:9px 12px}
|
|
278
|
-
.sm-rail__menu-title{font-size:9px;font-weight:600;letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--text-muted);padding:6px 8px 4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
279
|
-
.sm-rail__menu-item{
|
|
263
|
+
display:none;flex-direction:column;gap:1px;font-size:var(--text-md);font-weight:500;line-height:1.4;color:var(--text);
|
|
264
|
+
}
|
|
265
|
+
.sm-navbar__menu[data-open="true"]{display:flex}
|
|
266
|
+
.sm-navbar__menu--end{left:auto;right:0;width:240px}
|
|
267
|
+
.sm-navbar__menu--brand{width:240px}
|
|
268
|
+
/* An invisible bridge spans the 6px gap so the pointer can cross from Collections into its hover menu. */
|
|
269
|
+
.sm-navbar__menu--collections::before{content:"";position:absolute;left:0;right:0;top:-8px;height:8px}
|
|
270
|
+
.sm-navbar__menu-title{font-size:9px;font-weight:600;letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--text-muted);padding:6px 8px 4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
271
|
+
.sm-navbar__menu-item{
|
|
280
272
|
appearance:none;background:none;border:0;width:100%;display:flex;align-items:center;justify-content:space-between;
|
|
281
273
|
padding:7px 10px;border-radius:var(--radius-sm);color:inherit;font:inherit;cursor:pointer;text-align:left;text-decoration:none;
|
|
282
274
|
}
|
|
283
|
-
.sm-
|
|
284
|
-
.sm-
|
|
275
|
+
.sm-navbar__menu-item:hover,.sm-navbar__menu-item:focus-visible{background:var(--hover);color:inherit;outline:0;box-shadow:none}
|
|
276
|
+
.sm-navbar__menu-item[aria-current="page"],.sm-navbar__menu-item[aria-current="page"]:hover{background:var(--nav-active-bg);color:var(--nav-active-fg);font-weight:600}
|
|
277
|
+
.sm-navbar__menu-label{display:flex;align-items:center;gap:10px;min-width:0}
|
|
278
|
+
.sm-navbar__menu-label>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
279
|
+
.sm-navbar__menu-label svg{flex:none;stroke:currentColor;fill:none}
|
|
280
|
+
/* Collections and ⋮ are dark (2026-11-14): they carry the bar's own items, so they take its colours. */
|
|
281
|
+
.sm-navbar__menu--dark{background:var(--navbar-menu-bg);color:var(--navbar-fg);border:0;padding:6px;gap:2px}
|
|
282
|
+
.sm-navbar__menu--dark .sm-navbar__menu-item{padding:9px 12px}
|
|
283
|
+
.sm-navbar__menu--dark .sm-navbar__menu-item:hover,.sm-navbar__menu--dark .sm-navbar__menu-item:focus-visible{background:var(--navbar-hover-bg);color:var(--navbar-active-fg)}
|
|
284
|
+
.sm-navbar__menu--dark .sm-navbar__menu-item[aria-current="page"],.sm-navbar__menu--dark .sm-navbar__menu-item[aria-current="page"]:hover{background:var(--navbar-active-bg);color:var(--navbar-active-fg)}
|
|
285
285
|
.sm-when-light{display:var(--when-light);align-items:center;gap:8px}
|
|
286
286
|
.sm-when-dark{display:var(--when-dark);align-items:center;gap:8px}
|
|
287
287
|
/* .sm-nav-item survives as the inspector's link style (EditorView). */
|
|
@@ -488,6 +488,7 @@ html:has(.sm-admin),body:has(.sm-admin){overscroll-behavior:none}
|
|
|
488
488
|
/* Inline editing in the canvas (spec 2026-09-10). The outlines are drawn in
|
|
489
489
|
the overlay from the bridge's field rects; the toolbar floats above the
|
|
490
490
|
selection. */
|
|
491
|
+
.sm-dim{background:color-mix(in srgb,var(--canvas) 62%,transparent);pointer-events:none}
|
|
491
492
|
.sm-fieldhover{outline:1px solid color-mix(in srgb,var(--selection) 55%,transparent);outline-offset:2px;border-radius:2px;pointer-events:none}
|
|
492
493
|
.sm-fieldedit{outline:1.5px solid var(--selection);outline-offset:3px;border-radius:2px;pointer-events:none}
|
|
493
494
|
.sm-floatbar{
|
|
@@ -800,6 +801,13 @@ html:has(.sm-admin),body:has(.sm-admin){overscroll-behavior:none}
|
|
|
800
801
|
min-width:0;min-height:0;overflow:hidden;
|
|
801
802
|
}
|
|
802
803
|
.sm-canvas__scroll{flex:1;min-height:0;overflow:auto;padding:var(--canvas-pad)}
|
|
804
|
+
/* A global item's canvas (spec 2026-11-14): a row above the frame — where the item is used, and which page shows it. */
|
|
805
|
+
.sm-globalcanvas{display:flex;flex-direction:column;min-width:0;min-height:0}
|
|
806
|
+
.sm-globalcanvas>.sm-canvas{flex:1}
|
|
807
|
+
.sm-globalcanvas__bar{flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:0 4px 8px;min-height:28px}
|
|
808
|
+
.sm-globalcanvas__bar .sm-hint{margin:0}
|
|
809
|
+
.sm-globalcanvas__pick{display:flex;align-items:center;gap:8px}
|
|
810
|
+
.sm-globalcanvas__pick .sm-select{width:auto;height:30px;padding:0 10px}
|
|
803
811
|
.sm-page{
|
|
804
812
|
position:relative;background:var(--page-bg);border-radius:var(--page-radius);
|
|
805
813
|
box-shadow:var(--shadow-page);min-height:100%;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SMOODLY_VERSION = "0.0.
|
|
1
|
+
export declare const SMOODLY_VERSION = "0.0.62";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/write-version.mjs from package.json — do not edit.
|
|
2
|
-
export const SMOODLY_VERSION = "0.0.
|
|
2
|
+
export const SMOODLY_VERSION = "0.0.62";
|
|
@@ -26,7 +26,7 @@ export const people = smoodly.collection({
|
|
|
26
26
|
| Name | Description |
|
|
27
27
|
| --- | --- |
|
|
28
28
|
| `name` | What references and queries use. Keep it stable once entries exist. global, asset and media are reserved. |
|
|
29
|
-
| `title` | The label in the admin's
|
|
29
|
+
| `title` | The label in the admin's navigation and list. |
|
|
30
30
|
| `titleField` | The field whose value names an entry in lists and pickers. Without one, or while it is empty, an entry reads Untitled. |
|
|
31
31
|
| `fields` | Any builders. A field named slug gives entries a URL part; .localized() gives a field a value per language. |
|
|
32
32
|
| `path` | Serves each entry at its own URL under this segment. See Entry pages. |
|
|
@@ -38,7 +38,7 @@ Register the export in the `collections` list of `smoodly/collections.ts`, which
|
|
|
38
38
|
|
|
39
39
|
## Entries in the admin
|
|
40
40
|
|
|
41
|
-
The *Collections* item in the admin's
|
|
41
|
+
The *Collections* item in the admin's top bar opens a menu of every collection. A collection editors open all day can sit on the bar itself: add `admin: { showInNav: true }` to its schema and it appears after *Pages* under its title, and leaves the menu. A collection's list shows its entries with their title, status and when they changed, 20 to a page; *Previous* and *Next* under the rows move through a longer list, and *Per page* shows 100 or 500 instead, a choice the browser remembers. The box above the rows filters by the collection's `titleField`: it finds the entries whose title holds the text, in any language, and searches the whole collection, not the page on screen. A collection without a `titleField` has no filter. While a filter is on, a hand-ordered list does not drag. *+ New entry* creates an empty draft in the selected language and opens the form at once. The form autosaves, and carries the same *Publish* control as a page, so an entry is published per language like a page. *History* and *Discard changes* work the same way.
|
|
42
42
|
|
|
43
43
|
An entry cannot be deleted while a page, a section or another entry references it. The message says how many, and the media library shows the same for assets.
|
|
44
44
|
|
|
@@ -52,7 +52,7 @@ function LandingView({ zones }: Props<typeof landingSchema>) {
|
|
|
52
52
|
export const LandingPage = smoodly.page(landingSchema, LandingView);
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
The hero zone offers a choice of two openers and takes one of them. The body takes anything. The closing zone always shows the site's one quote, edited under *
|
|
55
|
+
The hero zone offers a choice of two openers and takes one of them. The body takes anything. The closing zone always shows the site's one quote, edited under *Sections*.
|
|
56
56
|
|
|
57
57
|
> **Note:** `sections: "*"` includes every registered section, the header and footer too. List the sections instead when some are only meant for the layout.
|
|
58
58
|
|
|
@@ -66,7 +66,7 @@ An empty zone shows as a box in the canvas so it can take a section. On the site
|
|
|
66
66
|
|
|
67
67
|
## Locking a zone to a global
|
|
68
68
|
|
|
69
|
-
`z.locked()` also takes a global with a section, as `closing` does above. The zone then shows that one global on every page of the type. Editors change it once, under *
|
|
69
|
+
`z.locked()` also takes a global with a section, as `closing` does above. The zone then shows that one global on every page of the type. Editors change it once, under *Sections* or right in any canvas that shows it, and every page follows. The starter's header and footer take a different route: the layout renders them directly, so they need no zone.
|
|
70
70
|
|
|
71
71
|
A global can also be placed by hand. The starter lists Quote in its body zones, so the picker's *Global* group offers the site quote there, and an editor may still add a one-off Quote from the sections list.
|
|
72
72
|
|
|
@@ -51,7 +51,7 @@ export default defineConfig({
|
|
|
51
51
|
});
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Publishing does not change the live site. Editors press **Deploy** in the admin's
|
|
54
|
+
Publishing does not change the live site. Editors press **Deploy** in the admin's top bar, next to their avatar, when they are done, and the host builds. Give the site the host's deploy hook as `SMOODLY_DEPLOY_HOOK` where `astro build` runs (on Vercel: Settings › Git › Deploy Hooks): the build hands it to Smoodly's API with the site's address; without it the tile shows what is waiting and Deploy stays disabled. `SITE_URL` must be the site's own address, and the build refuses static output without it: the tile reads the build's marker, `/_smoodly/build.json`, from there. It says *Building…* until the new build answers, *N changes* while publishes wait (an unpublish or a delete counts as one too), and *The build has not landed* after ten minutes with no new build, in which case look at the host's log. A host that strips query strings from a cached file can hold the old marker for its max-age.
|
|
55
55
|
|
|
56
56
|
The admin, its revalidate and preview routes and the preview address still run on demand, so the deployment is files plus one function; a host with no function at all cannot run the admin. A static page has no lists by query string and no draft at its real address: drafts are edited through the preview address, as in live mode. A moved page's old address becomes one of the host's redirects at build, so it is answered without a function too. The cache provider in `astro.config` is unused in static output and may stay.
|
|
57
57
|
|
|
@@ -20,7 +20,7 @@ Editors sign in at smoodly.io, never on your site: `/admin` sends them there and
|
|
|
20
20
|
|
|
21
21
|
## Who is editing what
|
|
22
22
|
|
|
23
|
-
A lock after a title in Pages, in a collection or in
|
|
23
|
+
A lock after a title in Pages, in a collection or in Sections means someone else has that item open in the language you are looking at. Hover it for their name. Opening the item anyway shows a line under the top bar: *aleksi is also editing this page.* Nothing is locked; it is a heads-up.
|
|
24
24
|
|
|
25
25
|
Presence runs on Smoodly's service, over one connection per open admin tab. Without it the admin works exactly the same, minus the locks and the line; a tab that loses its connection reconnects on its own.
|
|
26
26
|
|
package/docs/globals/defining.md
CHANGED
|
@@ -45,7 +45,7 @@ Register the list in the config's `globals`. A visual global's section must be i
|
|
|
45
45
|
|
|
46
46
|
## In the admin
|
|
47
47
|
|
|
48
|
-
*
|
|
48
|
+
*Sections*, under ⋮ in the admin's top bar, lists every global with its kind, *Visual* or *Fields*, and its status in the selected language. A *Fields* global opens a form like an entry's, the fields in schema order. A *Visual* global opens on a canvas: a page of the site that shows it, the home page for a `layout` global and otherwise the first page that places it, with the global selected and the rest of the page dimmed. Its fields sit beside the canvas under *Content* and *Settings*, text marked for inline editing can be typed in place, and when several pages place it, *Shown on* picks the page. A visual global no page shows yet opens as a form. Either way the page autosaves, and carries the same *Publish* control, *History* and *Discard changes* as a page.
|
|
49
49
|
|
|
50
50
|
A global exists in every language from the start, so there is nothing to add or delete. Publishing is per language, as for pages. A field's value is shared across languages unless it chains `.localized()`: the starter's contact email is one value, its address is translated.
|
|
51
51
|
|
|
@@ -25,7 +25,7 @@ export const headerSchema = smoodly.schema.section({
|
|
|
25
25
|
|
|
26
26
|
`styles: false` takes away the *Spacing & layout* group, because the header's spacing is the design's. The brand is shared across languages and the navigation is localized as a whole, so each language has its own labels and links. The footer follows the same pattern with a tagline, three link columns and a copyright line.
|
|
27
27
|
|
|
28
|
-
Both are registered in `smoodly/sections.ts` and named with `smoodly.global()` in `smoodly/globals.ts`, which gives each a row under *
|
|
28
|
+
Both are registered in `smoodly/sections.ts` and named with `smoodly.global()` in `smoodly/globals.ts`, which gives each a row under *Sections* and its `sample` as starting content. No zone lists them, so the *Add section* picker never offers them. Both are `layout: true` ([Defining a global](../globals/defining.md#layout-globals)): on Astro they arrive with the page's own read, so rendering them costs no extra call.
|
|
29
29
|
|
|
30
30
|
## Rendering them
|
|
31
31
|
|
|
@@ -112,6 +112,6 @@ On a single-language site there are no links and the header shows the brand and
|
|
|
112
112
|
|
|
113
113
|
## Editing them
|
|
114
114
|
|
|
115
|
-
Editors change the header and footer under *
|
|
115
|
+
Editors change the header and footer under *Sections*, or in any page's canvas: both are selectable there, tagged *global*, with their own form and their own *Publish* in the inspector. The header's navigation edits in place like any list with [marks](../content-modeling/marks.md). Publishing a page never publishes the footer, because a footer going live on every page is a deliberate click.
|
|
116
116
|
|
|
117
117
|
Next, [The media library](../media/library.md).
|
package/docs/media/library.md
CHANGED
|
@@ -4,7 +4,7 @@ Every image, video and file editors upload lives in one library. A field stores
|
|
|
4
4
|
|
|
5
5
|
## Uploading
|
|
6
6
|
|
|
7
|
-
Editors upload from two places. A media field takes a dropped file straight away, or opens the library to pick one. *Media* in the
|
|
7
|
+
Editors upload from two places. A media field takes a dropped file straight away, or opens the library to pick one. *Media* in the top bar shows the whole library, with an upload button and a filter for images, video and files.
|
|
8
8
|
|
|
9
9
|
The browser measures an image's dimensions on upload, and captures a frame of a video as its poster, stored as an image asset of its own. What the site can accept is config:
|
|
10
10
|
|
package/package.json
CHANGED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
// packages/smoodly/src/admin/shell/IconRail.tsx
|
|
4
|
-
// The admin's primary navigation: the design handoff's 72px IconRail (the
|
|
5
|
-
// cool-blue look's components/navigation/IconRail, in the tree until 2026-10-19;
|
|
6
|
-
// the shipped tokens are design_handoff_smoodly_neutral/), which replaced the
|
|
7
|
-
// 200px SideNav so the visual editor gets the width. Ported markup-for-markup;
|
|
8
|
-
// the handoff's click handlers are the shell's router.
|
|
9
|
-
//
|
|
10
|
-
// Items are registry-derived, never separately configured: Pages, then a
|
|
11
|
-
// rail item for each collection registered with `admin.showInNav`
|
|
12
|
-
// (2026-11-13), then Collections — one rail item whose flyout lists every
|
|
13
|
-
// other collection, absent when none is left — then
|
|
14
|
-
// Media, Globals (the global content items, when the registry has any;
|
|
15
|
-
// the route is /globals), and Settings (the Settings area, spec
|
|
16
|
-
// 2026-09-25; the route is /settings; every member has a page there since
|
|
17
|
-
// Redirects, 2026-09-26, so the shell passes `settings` once the role is
|
|
18
|
-
// known). The user avatar is pinned to the bottom and opens the account
|
|
19
|
-
// flyout: the email, the theme toggle, "My Account" (at smoodly.io) and Sign
|
|
20
|
-
// out (spec 2026-10-16 sign-in and the dashboard §6).
|
|
21
|
-
//
|
|
22
|
-
// Flyouts: click toggles, Esc closes and returns focus to the trigger,
|
|
23
|
-
// ↑/↓ move between entries, a pointer down outside closes. A child being
|
|
24
|
-
// active highlights its parent tile and the entry inside the menu.
|
|
25
|
-
import { useEffect, useId, useRef, useState } from "react";
|
|
26
|
-
import { ExternalLink, Globe, Image, Layers, LibraryBig, PanelTop, RadioTower, Settings } from "lucide-react";
|
|
27
|
-
import { tileCopy } from "./deploy-tile.js";
|
|
28
|
-
import { ThemeToggle } from "../ui/primitives.js";
|
|
29
|
-
import { useAdminHref } from "./base.js";
|
|
30
|
-
import { consoleLinks } from "./console-links.js";
|
|
31
|
-
import { SMOODLY_VERSION } from "../../version.js";
|
|
32
|
-
import { RouteLink } from "./RouteLink.js";
|
|
33
|
-
/** Lucide at the rail's size and stroke (the tile's CSS restates both). */
|
|
34
|
-
const ICON = { size: 18, strokeWidth: 2, "aria-hidden": true };
|
|
35
|
-
/** The app icon, inlined: a package can't ship a file into the host app's
|
|
36
|
-
* /public. The wordmark's "s" on a rounded square (chosen 2026-10-23; the
|
|
37
|
-
* same tile, green with a white s, is smoodly.io's favicon). The square takes
|
|
38
|
-
* --logo-outer and the glyph --logo-inner, so a look decides its colours. */
|
|
39
|
-
function AppIcon() {
|
|
40
|
-
return (_jsxs("svg", { className: "sm-rail__logo", viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", role: "img", "aria-label": "Smoodly", children: [_jsx("rect", { width: "64", height: "64", rx: "14", style: { fill: "var(--logo-outer)" } }), _jsx("path", { style: { fill: "var(--logo-inner)" }, transform: "translate(14.382 -4.365) scale(0.7123)", d: "M30.5851 22.9763C36.7891 22.9763 45.6052 24.6681 49.3603 26.5237L46.2583 42.2415C41.6869 40.113 35.2653 38.4212 29.4422 38.4212C26.0137 38.4212 23.7825 39.1852 23.7825 40.9862C23.7825 42.1869 24.9797 42.8964 27.1021 43.333L31.5647 44.3153C40.8162 46.3346 45.5508 51.1373 45.5508 58.505C45.5508 73.4587 33.9592 79.1345 18.939 79.1345C11.4833 79.1345 4.24536 77.3335 0.109375 75.6963L3.42905 58.9416C8.97998 61.8341 16.4356 63.6351 21.2247 63.6351C24.6532 63.6351 26.9933 62.7619 26.9933 60.6334C26.9933 59.4327 26.1226 58.505 23.5648 58.0684L19.7553 57.3043C12.5173 55.9399 6.31336 50.9735 6.31336 43.0055C6.31336 30.6169 15.6737 23.0309 30.6939 23.0309L30.5851 22.9763Z" })] }));
|
|
41
|
-
}
|
|
42
|
-
/** A menu link that opens smoodly.io in a new tab, with the arrow that says so. */
|
|
43
|
-
function ExtLink({ href, onClick, children }) {
|
|
44
|
-
return (_jsxs("a", { role: "menuitem", className: "sm-rail__menu-item", href: href, target: "_blank", rel: "noreferrer", onClick: onClick, children: [_jsx("span", { children: children }), _jsx(ExternalLink, { className: "sm-rail__ext", size: 13, strokeWidth: 1.75, "aria-hidden": "true" })] }));
|
|
45
|
-
}
|
|
46
|
-
export function IconRail({ registry, active, email, settings = true, deploy, onSignOut }) {
|
|
47
|
-
const adminHref = useAdminHref();
|
|
48
|
-
const root = useRef(null);
|
|
49
|
-
const uid = useId();
|
|
50
|
-
const [open, setOpen] = useState(null);
|
|
51
|
-
// Collections opens on hover as well (spec 2026-09-15 rail tweaks): a hover
|
|
52
|
-
// open closes when the pointer leaves and never steals focus; a click open
|
|
53
|
-
// is sticky and moves focus into the menu for the keyboard.
|
|
54
|
-
const [hovered, setHovered] = useState(false);
|
|
55
|
-
const menuId = (key) => `sm-rail-menu${uid}${key}`;
|
|
56
|
-
const toggle = (key) => setOpen((cur) => (cur === key ? null : key));
|
|
57
|
-
const close = () => setOpen(null);
|
|
58
|
-
const hoverLeave = () => setHovered(false);
|
|
59
|
-
// A click open moves focus into the menu; a pointer down outside closes it.
|
|
60
|
-
// Outside means outside the menu and its trigger — the rest of the rail
|
|
61
|
-
// counts too (2026-09-26); the trigger is left to its own toggle.
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
if (!open || !root.current)
|
|
64
|
-
return;
|
|
65
|
-
if (!hovered)
|
|
66
|
-
root.current.querySelector(`#${CSS.escape(menuId(open))} [role=menuitem]`)?.focus();
|
|
67
|
-
const onPointerDown = (e) => {
|
|
68
|
-
if (!(e.target instanceof Node))
|
|
69
|
-
return;
|
|
70
|
-
const menu = root.current?.querySelector(`#${CSS.escape(menuId(open))}`);
|
|
71
|
-
const trigger = root.current?.querySelector(`[aria-controls="${menuId(open)}"]`);
|
|
72
|
-
if (menu?.contains(e.target) || trigger?.contains(e.target))
|
|
73
|
-
return;
|
|
74
|
-
close();
|
|
75
|
-
};
|
|
76
|
-
document.addEventListener("pointerdown", onPointerDown);
|
|
77
|
-
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
78
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
79
|
-
}, [open]);
|
|
80
|
-
const onKeyDown = (e) => {
|
|
81
|
-
if (!open || !root.current)
|
|
82
|
-
return;
|
|
83
|
-
if (e.key === "Escape") {
|
|
84
|
-
close();
|
|
85
|
-
root.current.querySelector(`[aria-controls="${menuId(open)}"]`)?.focus();
|
|
86
|
-
}
|
|
87
|
-
else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
88
|
-
const items = Array.from(root.current.querySelectorAll(`#${CSS.escape(menuId(open))} [role=menuitem]`));
|
|
89
|
-
if (!items.length)
|
|
90
|
-
return;
|
|
91
|
-
e.preventDefault();
|
|
92
|
-
const i = items.indexOf(document.activeElement);
|
|
93
|
-
items[(i + (e.key === "ArrowDown" ? 1 : -1) + items.length) % items.length].focus();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
const link = (key, label, icon, href) => (_jsx("li", { className: "sm-rail__item", children: _jsxs(RouteLink, { className: "sm-rail__btn", href: href, "aria-current": key === active ? "page" : undefined, onClick: close, children: [_jsx("span", { className: "sm-rail__tile", children: icon }), _jsx("span", { className: "sm-rail__label", title: label, children: label })] }) }, key));
|
|
97
|
-
// A collection with `admin.showInNav` is a rail item of its own; the rest stay in the Collections menu.
|
|
98
|
-
const pinned = registry.collections.filter((c) => c.inNav);
|
|
99
|
-
const collections = registry.collections.filter((c) => !c.inNav);
|
|
100
|
-
const collectionActive = collections.some((c) => c.name === active);
|
|
101
|
-
const collectionsOpen = open === "collections" || (hovered && open === null);
|
|
102
|
-
const name = email?.split("@")[0] ?? "Editor";
|
|
103
|
-
const initials = name.slice(0, 2).toUpperCase();
|
|
104
|
-
return (_jsxs("nav", { className: "sm-rail", "aria-label": "Primary", ref: root, onKeyDown: onKeyDown, children: [_jsxs("div", { className: "sm-rail__item", children: [_jsx("button", { type: "button", className: "sm-rail__brand", title: "Smoodly", "aria-haspopup": "menu", "aria-expanded": open === "brand", "aria-controls": menuId("brand"), onClick: () => toggle("brand"), children: _jsx(AppIcon, {}) }), _jsxs("ul", { className: "sm-rail__menu sm-rail__menu--brand", role: "menu", id: menuId("brand"), "data-open": open === "brand", children: [_jsx("li", { role: "none", children: _jsx(ExtLink, { href: consoleLinks().project, onClick: close, children: "This site at smoodly.io" }) }), _jsx("li", { role: "none", children: _jsx(ExtLink, { href: consoleLinks().docs, onClick: close, children: "Documentation" }) }), _jsxs("li", { className: "sm-rail__menu-version", role: "presentation", children: ["Smoodly ", SMOODLY_VERSION] })] })] }), _jsxs("ul", { className: "sm-rail__list", children: [link("pages", "Pages", _jsx(PanelTop, { ...ICON }), adminHref("/pages")), pinned.map((c) => link(c.name, c.title, _jsx(Layers, { ...ICON }), adminHref(`/${c.name}`))), collections.length > 0 && (_jsxs("li", { className: "sm-rail__item", onMouseEnter: () => setHovered(true), onMouseLeave: hoverLeave, children: [_jsxs("button", { type: "button", className: "sm-rail__btn", "aria-current": collectionActive ? "page" : undefined, "aria-haspopup": "menu", "aria-expanded": collectionsOpen, "aria-controls": menuId("collections"), onClick: () => toggle("collections"), children: [_jsx("span", { className: "sm-rail__tile", children: _jsx(LibraryBig, { ...ICON }) }), _jsx("span", { className: "sm-rail__label", children: "Collections" })] }), _jsx("ul", { className: "sm-rail__menu sm-rail__menu--collections", role: "menu", id: menuId("collections"), "data-open": collectionsOpen, children: collections.map((c) => (_jsx("li", { role: "none", children: _jsx(RouteLink, { role: "menuitem", className: "sm-rail__menu-item", href: adminHref(`/${c.name}`), "aria-current": c.name === active ? "page" : undefined, onClick: () => { close(); hoverLeave(); }, children: _jsx("span", { children: c.title }) }) }, c.name))) })] })), link("media", "Media", _jsx(Image, { ...ICON }), adminHref("/media")), registry.globals.length > 0 && link("globals", "Site", _jsx(Globe, { ...ICON }), adminHref("/globals")), settings && link("settings", "Settings", _jsx(Settings, { ...ICON }), adminHref("/settings"))] }), deploy && (() => {
|
|
105
|
-
const copy = tileCopy(deploy.status, Date.now());
|
|
106
|
-
const id = menuId("deploy");
|
|
107
|
-
return (_jsxs("div", { className: "sm-rail__item sm-rail__deploy", "data-state": deploy.status.state, children: [_jsxs("button", { type: "button", className: "sm-rail__btn", "aria-haspopup": "menu", "aria-expanded": open === "deploy", "aria-controls": id, onClick: () => toggle("deploy"), title: copy.detail, children: [_jsxs("span", { className: `sm-rail__tile${copy.lit ? " sm-rail__tile--lit" : ""}${copy.warn ? " sm-rail__tile--warn" : ""}`, children: [_jsx(RadioTower, { ...ICON }), copy.badge && _jsx("span", { className: "sm-rail__badge", "aria-label": `${copy.badge} changes waiting`, children: copy.badge })] }), _jsx("span", { className: "sm-rail__label", children: copy.label })] }), _jsxs("ul", { className: "sm-rail__menu sm-rail__menu--up", role: "menu", id: id, "data-open": open === "deploy", children: [_jsx("li", { className: "sm-rail__menu-detail", role: "presentation", children: copy.detail }), _jsx("li", { role: "none", children: _jsx("button", { type: "button", role: "menuitem", className: "sm-rail__menu-item", disabled: copy.action === null || deploy.busy, title: copy.disabledReason ?? undefined, onClick: () => { close(); deploy.onDeploy(); }, children: copy.action === "retry" ? "Deploy again" : "Deploy now" }) }), copy.disabledReason && _jsx("li", { className: "sm-rail__menu-note", role: "presentation", children: copy.disabledReason })] })] }));
|
|
108
|
-
})(), _jsxs("div", { className: "sm-rail__item sm-rail__foot", children: [_jsx("button", { type: "button", className: "sm-rail__user", title: name, "aria-haspopup": "menu", "aria-expanded": open === "user", "aria-controls": menuId("user"), onClick: () => toggle("user"), children: initials }), _jsxs("ul", { className: "sm-rail__menu sm-rail__menu--up", role: "menu", id: menuId("user"), "data-open": open === "user", children: [_jsx("li", { className: "sm-rail__menu-title", role: "presentation", children: email ?? name }), _jsx("li", { role: "none", children: _jsx(ThemeToggle, {}) }), _jsx("li", { role: "none", children: _jsx(ExtLink, { href: consoleLinks().account, onClick: close, children: "My Account" }) }), _jsx("li", { role: "none", children: _jsx("button", { type: "button", role: "menuitem", className: "sm-rail__menu-item", onClick: () => { close(); onSignOut(); }, children: "Sign out" }) })] })] })] }));
|
|
109
|
-
}
|