smoodly 0.0.23 → 0.0.25
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 +192 -43
- package/bin/smoodly.js +6 -0
- package/dist/admin/auth.d.ts +10 -5
- package/dist/admin/auth.js +14 -13
- package/dist/admin/client-ops.js +2 -1
- package/dist/admin/editor/EditorView.js +20 -6
- package/dist/admin/editor/InsertPicker.d.ts +14 -0
- package/dist/admin/editor/InsertPicker.js +37 -7
- package/dist/admin/editor/InspectorFields.js +1 -1
- package/dist/admin/editor/Outline.js +2 -2
- package/dist/admin/editor/PageSettings.js +6 -0
- package/dist/admin/editor/SeoSettings.js +1 -1
- package/dist/admin/editor/locale-url.d.ts +7 -4
- package/dist/admin/editor/locale-url.js +14 -7
- package/dist/admin/editor/usePageLocale.js +9 -1
- package/dist/admin/forms/CropEditor.d.ts +4 -2
- package/dist/admin/forms/CropEditor.js +6 -3
- package/dist/admin/forms/DatePicker.d.ts +9 -0
- package/dist/admin/forms/DatePicker.js +133 -0
- package/dist/admin/forms/FieldWidget.js +11 -5
- package/dist/admin/forms/ImagePreview.d.ts +9 -0
- package/dist/admin/forms/ImagePreview.js +11 -0
- package/dist/admin/forms/MediaWidget.d.ts +8 -2
- package/dist/admin/forms/MediaWidget.js +13 -13
- package/dist/admin/forms/RichtextElementCard.js +1 -1
- package/dist/admin/forms/RichtextImageCard.js +14 -9
- package/dist/admin/forms/date-picker.d.ts +44 -0
- package/dist/admin/forms/date-picker.js +117 -0
- package/dist/admin/forms/elements-context.d.ts +3 -0
- package/dist/admin/forms/elements-context.js +7 -2
- package/dist/admin/forms/time-zone-context.d.ts +7 -0
- package/dist/admin/forms/time-zone-context.js +15 -0
- package/dist/admin/forms/useRichtextEditor.js +10 -1
- package/dist/admin/index.d.ts +2 -0
- package/dist/admin/next/bridge.d.ts +2 -1
- package/dist/admin/next/bridge.js +35 -5
- package/dist/admin/next/index.d.ts +2 -0
- package/dist/admin/ops-impl.js +73 -1
- package/dist/admin/ops.d.ts +22 -2
- package/dist/admin/serialize.d.ts +2 -0
- package/dist/admin/serialize.js +1 -0
- package/dist/admin/shell/AdminApp.js +48 -3
- package/dist/admin/shell/EntriesList.js +12 -21
- package/dist/admin/shell/EntryForm.js +63 -5
- package/dist/admin/shell/GlobalForm.js +22 -2
- package/dist/admin/shell/GlobalList.js +4 -18
- package/dist/admin/shell/IconRail.d.ts +3 -1
- package/dist/admin/shell/IconRail.js +17 -7
- package/dist/admin/shell/InviteModal.d.ts +6 -0
- package/dist/admin/shell/InviteModal.js +28 -0
- package/dist/admin/shell/ListView.js +2 -2
- package/dist/admin/shell/LoginView.js +34 -5
- package/dist/admin/shell/MediaScreen.js +11 -1
- package/dist/admin/shell/MembersPage.d.ts +2 -1
- package/dist/admin/shell/MembersPage.js +90 -25
- package/dist/admin/shell/PageDialogs.d.ts +26 -0
- package/dist/admin/shell/PageDialogs.js +38 -0
- package/dist/admin/shell/PagesList.js +100 -70
- package/dist/admin/shell/RouteLink.js +1 -1
- package/dist/admin/shell/SchemaNotice.d.ts +4 -0
- package/dist/admin/shell/SchemaNotice.js +4 -0
- package/dist/admin/shell/SetPasswordView.d.ts +7 -0
- package/dist/admin/shell/SetPasswordView.js +41 -0
- package/dist/admin/shell/SettingsView.d.ts +5 -1
- package/dist/admin/shell/SettingsView.js +17 -11
- package/dist/admin/shell/live-url.d.ts +10 -0
- package/dist/admin/shell/live-url.js +19 -0
- package/dist/admin/shell/locale-memory.d.ts +8 -0
- package/dist/admin/shell/locale-memory.js +23 -0
- package/dist/admin/shell/members-list.d.ts +15 -1
- package/dist/admin/shell/members-list.js +24 -7
- package/dist/admin/shell/page-dialogs.d.ts +9 -0
- package/dist/admin/shell/page-dialogs.js +19 -0
- package/dist/admin/shell/router.d.ts +10 -7
- package/dist/admin/shell/router.js +18 -9
- package/dist/admin/shell/session.d.ts +6 -0
- package/dist/admin/shell/session.js +17 -2
- package/dist/admin/shell/settings.d.ts +0 -6
- package/dist/admin/shell/settings.js +4 -5
- package/dist/admin/supabase-auth.js +6 -5
- package/dist/admin/tree-ops.js +3 -0
- package/dist/admin/ui/ActionMenu.js +26 -6
- package/dist/admin/ui/Combobox.d.ts +5 -0
- package/dist/admin/ui/Combobox.js +1 -1
- package/dist/admin/ui/ConfirmDialog.d.ts +8 -0
- package/dist/admin/ui/ConfirmDialog.js +30 -0
- package/dist/admin/ui/Dialog.d.ts +13 -0
- package/dist/admin/ui/Dialog.js +54 -0
- package/dist/admin/ui/HistoryModal.d.ts +2 -1
- package/dist/admin/ui/HistoryModal.js +7 -2
- package/dist/admin/ui/ResizeHandle.js +4 -4
- package/dist/admin/ui/confirm.d.ts +35 -0
- package/dist/admin/ui/confirm.js +57 -0
- package/dist/admin/ui/primitives.d.ts +19 -4
- package/dist/admin/ui/primitives.js +11 -5
- package/dist/admin/ui/resize.d.ts +7 -4
- package/dist/admin/ui/resize.js +7 -4
- package/dist/admin/ui/theme.d.ts +1 -1
- package/dist/admin/ui/theme.js +368 -203
- package/dist/admin/validate.d.ts +4 -0
- package/dist/admin/validate.js +28 -0
- package/dist/cli/load-collections.d.ts +2 -0
- package/dist/cli/load-collections.js +26 -0
- package/dist/cli/main.d.ts +13 -0
- package/dist/cli/main.js +62 -0
- package/dist/cli/migrate.d.ts +23 -0
- package/dist/cli/migrate.js +64 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +9 -0
- package/dist/fields.d.ts +2 -0
- package/dist/fields.js +2 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -2
- package/dist/marks.d.ts +3 -0
- package/dist/marks.js +3 -0
- package/dist/member-store.d.ts +30 -1
- package/dist/member-store.js +37 -6
- package/dist/migrations.d.ts +37 -0
- package/dist/migrations.js +73 -0
- package/dist/permissions.d.ts +7 -0
- package/dist/permissions.js +19 -0
- package/dist/schema-version.d.ts +17 -0
- package/dist/schema-version.js +32 -0
- package/dist/sql-space.js +6 -0
- package/dist/sql.js +10 -0
- package/dist/store.d.ts +3 -0
- package/dist/store.js +4 -0
- package/dist/supabase-member-store.d.ts +8 -1
- package/dist/supabase-member-store.js +62 -2
- package/dist/supabase-store.d.ts +3 -0
- package/dist/supabase-store.js +23 -0
- package/dist/time-zone.d.ts +6 -0
- package/dist/time-zone.js +27 -0
- package/migrations/20260926120000_smoodly_v1.sql +535 -0
- package/package.json +13 -3
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// The admin's one "are you sure" (design_handoff_smoodly_neutral/screens/
|
|
2
|
+
// ConfirmModal.md): a caller awaits `confirm(...)` and gets a boolean, the
|
|
3
|
+
// way it awaited window.confirm — but the question is answered in
|
|
4
|
+
// Smoodly's own dialog, rendered by <ConfirmHost/> (ConfirmDialog.tsx)
|
|
5
|
+
// from this store. Framework-free on purpose: the router (shell/router.ts)
|
|
6
|
+
// asks from outside React.
|
|
7
|
+
//
|
|
8
|
+
// One question at a time; a second request waits for the first answer.
|
|
9
|
+
// Without a mounted host (a test, a stray render outside the shell) the
|
|
10
|
+
// question falls back to window.confirm so no caller can hang.
|
|
11
|
+
/** The Discard changes question, shared by the page editor and the entry and global forms (publish flow §4). */
|
|
12
|
+
export const DISCARD_CONFIRM = {
|
|
13
|
+
title: "Discard unsaved changes?",
|
|
14
|
+
body: "The draft goes back to the published version. Every edit since the last publish is lost.",
|
|
15
|
+
confirmLabel: "Discard changes",
|
|
16
|
+
destructive: true,
|
|
17
|
+
};
|
|
18
|
+
const listeners = new Set();
|
|
19
|
+
let queue = [];
|
|
20
|
+
let seq = 0;
|
|
21
|
+
const notify = () => { for (const fn of listeners)
|
|
22
|
+
fn(); };
|
|
23
|
+
/** Ask; resolves with the answer once the dialog closes. */
|
|
24
|
+
export function confirm(options) {
|
|
25
|
+
if (listeners.size === 0) {
|
|
26
|
+
const text = [options.title, options.body].filter(Boolean).join("\n\n");
|
|
27
|
+
return Promise.resolve(typeof window !== "undefined" && typeof window.confirm === "function" ? window.confirm(text) : false);
|
|
28
|
+
}
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const id = ++seq;
|
|
31
|
+
const request = {
|
|
32
|
+
id,
|
|
33
|
+
options,
|
|
34
|
+
answer(yes) {
|
|
35
|
+
if (!queue.some((r) => r.id === id))
|
|
36
|
+
return; // answered already
|
|
37
|
+
queue = queue.filter((r) => r.id !== id);
|
|
38
|
+
notify();
|
|
39
|
+
resolve(yes);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
queue = [...queue, request];
|
|
43
|
+
notify();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** The question being asked, if any — the head of the queue. */
|
|
47
|
+
export const pendingConfirm = () => queue[0] ?? null;
|
|
48
|
+
/** For useSyncExternalStore: fires on every queue change. */
|
|
49
|
+
export function subscribeConfirm(fn) {
|
|
50
|
+
listeners.add(fn);
|
|
51
|
+
return () => { listeners.delete(fn); };
|
|
52
|
+
}
|
|
53
|
+
/** Test seam: drop every pending question unanswered. */
|
|
54
|
+
export function resetConfirms() {
|
|
55
|
+
queue = [];
|
|
56
|
+
notify();
|
|
57
|
+
}
|
|
@@ -13,13 +13,16 @@ export declare function Logo({ height }: {
|
|
|
13
13
|
* is right on the very first paint with no state and no hydration mismatch. */
|
|
14
14
|
export declare function ThemeToggle(): import("react").JSX.Element;
|
|
15
15
|
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
16
|
-
|
|
16
|
+
/** `danger` is the quiet red-text button; `destructive` the filled red primary of a confirm dialog. */
|
|
17
|
+
variant?: "primary" | "secondary" | "ghost" | "danger" | "destructive" | "ok";
|
|
17
18
|
size?: "sm" | "md" | "lg";
|
|
18
19
|
};
|
|
19
20
|
export declare function Button({ variant, size, className, ...rest }: ButtonProps): import("react").JSX.Element;
|
|
20
21
|
/** The same shape as Button, for navigations that must stay real links. */
|
|
21
|
-
export declare function LinkButton({ variant, children, ...rest }: {
|
|
22
|
+
export declare function LinkButton({ variant, icon, children, ...rest }: {
|
|
22
23
|
variant?: ButtonProps["variant"];
|
|
24
|
+
/** An icon-only pill (a square the button's height); give it an aria-label. */
|
|
25
|
+
icon?: boolean;
|
|
23
26
|
children: ReactNode;
|
|
24
27
|
} & AnchorHTMLAttributes<HTMLAnchorElement>): import("react").JSX.Element;
|
|
25
28
|
export declare function IconButton({ label, tone, size, children, ...rest }: {
|
|
@@ -102,6 +105,17 @@ export declare function Segmented<T extends string>({ options, value, onChange,
|
|
|
102
105
|
className?: string;
|
|
103
106
|
} | undefined;
|
|
104
107
|
}): import("react").JSX.Element;
|
|
108
|
+
/** A pill row (the f.radio look, spec 2026-09-14 §5): no track, the chosen pill filled in the
|
|
109
|
+
* soft accent. For a small choice inside a table row, where a segmented control reads as tabs. */
|
|
110
|
+
export declare function Pills<T extends string>({ options, value, onChange, disabled, title, label }: {
|
|
111
|
+
options: readonly T[];
|
|
112
|
+
value: T;
|
|
113
|
+
onChange(v: T): void;
|
|
114
|
+
disabled?: boolean;
|
|
115
|
+
/** Why the row is off, on every pill. */
|
|
116
|
+
title?: string;
|
|
117
|
+
label?(o: T): ReactNode;
|
|
118
|
+
}): import("react").JSX.Element;
|
|
105
119
|
export declare function StatusBadge({ status }: {
|
|
106
120
|
status: string;
|
|
107
121
|
}): import("react").JSX.Element;
|
|
@@ -109,10 +123,11 @@ export declare function StatusBadge({ status }: {
|
|
|
109
123
|
export declare function SaveIndicator({ state }: {
|
|
110
124
|
state: SaveState;
|
|
111
125
|
}): import("react").JSX.Element;
|
|
112
|
-
|
|
126
|
+
/** `caps`: the overline style — small, uppercase, tracked (2026-09-26; the mono face before). */
|
|
127
|
+
export declare function SectionLabel({ children, action, caps, style }: {
|
|
113
128
|
children: ReactNode;
|
|
114
129
|
action?: ReactNode;
|
|
115
|
-
|
|
130
|
+
caps?: boolean;
|
|
116
131
|
style?: CSSProperties;
|
|
117
132
|
}): import("react").JSX.Element;
|
|
118
133
|
export declare function Divider({ bleed }: {
|
|
@@ -45,8 +45,8 @@ export function Button({ variant = "primary", size = "md", className, ...rest })
|
|
|
45
45
|
return (_jsx("button", { type: "button", className: cx("sm-btn", `sm-btn--${variant}`, size === "lg" && "sm-btn--lg", size === "sm" && "sm-btn--sm", className), ...rest }));
|
|
46
46
|
}
|
|
47
47
|
/** The same shape as Button, for navigations that must stay real links. */
|
|
48
|
-
export function LinkButton({ variant = "secondary", children, ...rest }) {
|
|
49
|
-
return _jsx("a", { className: cx("sm-btn", `sm-btn--${variant}
|
|
48
|
+
export function LinkButton({ variant = "secondary", icon = false, children, ...rest }) {
|
|
49
|
+
return _jsx("a", { className: cx("sm-btn", `sm-btn--${variant}`, icon && "sm-btn--icon"), ...rest, children: children });
|
|
50
50
|
}
|
|
51
51
|
export function IconButton({ label, tone = "default", size = 24, children, ...rest }) {
|
|
52
52
|
return (_jsx("button", { type: "button", "aria-label": label, title: label, className: cx("sm-iconbtn", tone === "inverse" && "sm-iconbtn--inverse"), style: { width: size, height: size }, ...rest, children: children }));
|
|
@@ -105,6 +105,11 @@ export function Segmented({ options, value, onChange, stretch, disabled, label,
|
|
|
105
105
|
return (_jsx("button", { type: "button", className: cx("sm-seg__opt", extra?.className), "aria-pressed": o === value, disabled: disabled || extra?.disabled, title: extra?.title, onClick: () => onChange(o), children: label ? label(o) : o }, o));
|
|
106
106
|
}) }));
|
|
107
107
|
}
|
|
108
|
+
/** A pill row (the f.radio look, spec 2026-09-14 §5): no track, the chosen pill filled in the
|
|
109
|
+
* soft accent. For a small choice inside a table row, where a segmented control reads as tabs. */
|
|
110
|
+
export function Pills({ options, value, onChange, disabled, title, label }) {
|
|
111
|
+
return (_jsx("div", { className: cx("sm-pills", disabled && "sm-pills--disabled"), role: "radiogroup", children: options.map((o) => (_jsx("button", { type: "button", className: "sm-pill", "aria-pressed": o === value, disabled: disabled, title: title, onClick: () => onChange(o), children: label ? label(o) : o }, o))) }));
|
|
112
|
+
}
|
|
108
113
|
/* ── data ────────────────────────────────────────────────────────────── */
|
|
109
114
|
// Keyed by the product labels of ui/status-label.ts (lowercased by StatusBadge).
|
|
110
115
|
const STATUS_TONE = {
|
|
@@ -126,10 +131,11 @@ export function SaveIndicator({ state }) {
|
|
|
126
131
|
error: { label: "Save failed — retrying", dot: "var(--danger)", color: "var(--danger)" },
|
|
127
132
|
};
|
|
128
133
|
const { label, dot, color } = map[state];
|
|
129
|
-
return (_jsxs("span", { className: "sm-savestate", style: { color }, title: label, children: [_jsx("span", { className: "sm-dot", style: { background: dot } }), label] }));
|
|
134
|
+
return (_jsxs("span", { className: "sm-savestate", "data-testid": "save-status", style: { color }, title: label, children: [_jsx("span", { className: "sm-dot", style: { background: dot } }), label] }));
|
|
130
135
|
}
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
/** `caps`: the overline style — small, uppercase, tracked (2026-09-26; the mono face before). */
|
|
137
|
+
export function SectionLabel({ children, action, caps, style }) {
|
|
138
|
+
return (_jsxs("div", { className: cx("sm-sectionlabel", caps && "sm-sectionlabel--caps"), style: style, children: [_jsx("span", { className: "sm-label", children: children }), action && _jsx("span", { style: { color: "var(--accent)", fontWeight: 600, fontSize: "var(--text-sm)" }, children: action })] }));
|
|
133
139
|
}
|
|
134
140
|
export function Divider({ bleed = 0 }) {
|
|
135
141
|
return _jsx("div", { className: "sm-divider", style: { margin: `0 -${bleed}px` } });
|
|
@@ -6,10 +6,13 @@ export declare const MIN_INSPECTOR_W = 260;
|
|
|
6
6
|
export declare const DEFAULT_INSPECTOR_W = 360;
|
|
7
7
|
/** One arrow-key press. */
|
|
8
8
|
export declare const RESIZE_STEP = 16;
|
|
9
|
-
/**
|
|
10
|
-
export declare const
|
|
11
|
-
/**
|
|
12
|
-
|
|
9
|
+
/** What the canvas keeps however far the inspector is dragged (2026-09-26, up from half the row; 340 for an hour). */
|
|
10
|
+
export declare const MIN_CANVAS_W = 440;
|
|
11
|
+
/** The editor row (canvas + handle + inspector) less the handle and the
|
|
12
|
+
* canvas minimum, never below the inspector minimum. */
|
|
13
|
+
export declare const maxInspectorW: (rowWidth: number, handleWidth?: number) => number;
|
|
14
|
+
/** An integer inside [MIN, the row less the handle and the canvas minimum]. */
|
|
15
|
+
export declare const clampInspectorW: (px: number, rowWidth: number, handleWidth?: number) => number;
|
|
13
16
|
/** A stored value is trusted only as a plain integer ≥ MIN; anything else is
|
|
14
17
|
* ignored (never clamped or rewritten). The boot script in theme.tsx repeats
|
|
15
18
|
* this test inline — keep the two in step. */
|
package/dist/admin/ui/resize.js
CHANGED
|
@@ -8,10 +8,13 @@ export const MIN_INSPECTOR_W = 260;
|
|
|
8
8
|
export const DEFAULT_INSPECTOR_W = 360;
|
|
9
9
|
/** One arrow-key press. */
|
|
10
10
|
export const RESIZE_STEP = 16;
|
|
11
|
-
/**
|
|
12
|
-
export const
|
|
13
|
-
/**
|
|
14
|
-
|
|
11
|
+
/** What the canvas keeps however far the inspector is dragged (2026-09-26, up from half the row; 340 for an hour). */
|
|
12
|
+
export const MIN_CANVAS_W = 440;
|
|
13
|
+
/** The editor row (canvas + handle + inspector) less the handle and the
|
|
14
|
+
* canvas minimum, never below the inspector minimum. */
|
|
15
|
+
export const maxInspectorW = (rowWidth, handleWidth = 0) => Math.max(MIN_INSPECTOR_W, Math.floor(rowWidth - handleWidth - MIN_CANVAS_W));
|
|
16
|
+
/** An integer inside [MIN, the row less the handle and the canvas minimum]. */
|
|
17
|
+
export const clampInspectorW = (px, rowWidth, handleWidth = 0) => Math.min(maxInspectorW(rowWidth, handleWidth), Math.max(MIN_INSPECTOR_W, Math.round(px)));
|
|
15
18
|
/** A stored value is trusted only as a plain integer ≥ MIN; anything else is
|
|
16
19
|
* ignored (never clamped or rewritten). The boot script in theme.tsx repeats
|
|
17
20
|
* this test inline — keep the two in step. */
|
package/dist/admin/ui/theme.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const ADMIN_CSS: string;
|
|
2
2
|
/** Where the theme toggle's choice is remembered, browser-side only. */
|
|
3
3
|
export declare const THEME_KEY = "smoodly-theme";
|
|
4
|
-
/**
|
|
4
|
+
/** Which look the developer is comparing (see the warm block): "warm" applies it, anything else is the default. */
|
|
5
5
|
export declare const LOOK_KEY = "smoodly-look";
|
|
6
6
|
/** One <style> element and the theme boot, rendered once by AdminLayout. */
|
|
7
7
|
export declare function AdminStyles(): import("react").JSX.Element;
|