@sevenfold/setto-client 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/README.md CHANGED
@@ -8,29 +8,34 @@ Editors authenticate via Supabase, edit text inline on the live page, pick secti
8
8
 
9
9
  ## Install
10
10
 
11
- ```bash
12
- bun add @setto/client@npm:@sevenfold/setto-client
11
+ Sevenfold sites pin a git tag, not an npm version, so changes ship without an npm round-trip:
12
+
13
+ ```json
14
+ "@setto/client": "github:nitech/setto-client#v0.5.0"
13
15
  ```
14
16
 
15
- Or add directly to `package.json`:
17
+ Bun clones the repo on `bun install`, runs the package's `prepare` script to build `dist/`, and links it as `@setto/client`. Vercel does the same automatically when it installs build deps.
16
18
 
17
- ```json
18
- "@setto/client": "npm:@sevenfold/setto-client@^0.1.0"
19
+ The package is also still published to npm (`@sevenfold/setto-client`) for external consumers:
20
+
21
+ ```bash
22
+ bun add @setto/client@npm:@sevenfold/setto-client
19
23
  ```
20
24
 
21
25
  Peer deps: `react`, `react-dom`, `react-i18next`, `i18next`.
22
26
 
23
- For local monorepo development alongside `carryon.no`, a `file:` link also works — see [Local development](#local-development-sevenfold-monorepo).
24
-
25
- ### Publishing a new version
27
+ For local monorepo development the consumer's `vite.config.ts` aliases `@setto/client` to setto-client source — see [Local development](#local-development-sevenfold-monorepo).
26
28
 
27
- 1. Bump `version` in `package.json`
28
- 2. Commit and push a tag: `git tag v0.1.1 && git push origin v0.1.1`
29
- 3. GitHub Actions publishes to npm automatically
29
+ ### Releasing a new version
30
30
 
31
- Requires an `NPM_TOKEN` secret in the GitHub repo (Granular Access Token from npmjs.com with write access to `@sevenfold`).
31
+ 1. Bump `version` in `package.json` following semver (patch / minor / major).
32
+ 2. Commit and tag: `git tag v0.5.1 && git push origin main --follow-tags`.
33
+ 3. In each consumer (`carryon.no`, `setto-site`):
34
+ - Update the tag in `package.json` (e.g. `#v0.5.0` → `#v0.5.1`).
35
+ - Run `bun install` to refresh the lockfile.
36
+ - Push — Vercel pulls the new tag on the next deploy.
32
37
 
33
- Or trigger manually via **Actions Publish to npm Run workflow**.
38
+ GitHub Actions still publishes to npm automatically when `package.json` lands on `main` (requires `NPM_TOKEN` secret), but Sevenfold consumers no longer wait for it.
34
39
 
35
40
  ---
36
41
 
@@ -69,7 +74,7 @@ createRoot(document.getElementById('root')!).render(
69
74
 
70
75
  ### 2. Mount the Setto route
71
76
 
72
- Editors reach edit mode by visiting `sitenavn.no/setto`.
77
+ Editors sign in once at `sitenavn.no/setto`. After that, every visit to the public site auto-enters edit mode for as long as the Supabase session persists.
73
78
 
74
79
  ```tsx
75
80
  // App.tsx
@@ -126,24 +131,32 @@ function ValuesSection() {
126
131
 
127
132
  ## Edit mode
128
133
 
129
- Edit mode activates when **both** are true:
134
+ Edit mode activates whenever **all** of these are true:
130
135
 
131
- 1. Authenticated Supabase session.
132
- 2. URL contains `?setto=edit`.
136
+ 1. The user has an authenticated Supabase session.
137
+ 2. The user has access to `config.siteId` (a row in the `sites` table that they can read).
138
+ 3. The current path is not `/setto` (the dashboard is rendered without inline editing).
133
139
 
134
- The `/setto` dashboard does **not** activate edit mode. Use **Begynn å redigere**, which navigates to `/?setto=edit`.
140
+ Sign-in at `/setto` persists via Supabase, so subsequent visits drop straight into edit mode no URL flag, no extra step.
135
141
 
136
142
  ### What editors see
137
143
 
144
+ A small round Setto button floats in the bottom-right corner. While there are no unpublished changes it stays as a circle showing only the Setto mark. As soon as you edit something it grows into a pill containing **Publiser** and a **⋯** menu with:
145
+
146
+ | Item | Effect |
147
+ |------|--------|
148
+ | **Avbryt** | Discards every unsaved draft (text, section colours, image uploads) |
149
+ | **Logg ut** | Signs out via Supabase and exits edit mode |
150
+ | **Historikk** | Opens the `/setto` dashboard with deployment history |
151
+
138
152
  | Action | How |
139
153
  |--------|-----|
140
154
  | Edit text | Click any `<T>` element — it becomes `contentEditable` |
141
155
  | Edit section colours | Click a section or block background (not text) |
142
156
  | Follow a link | Ctrl/Cmd + click (desktop) · **Naviger ↗** chip when focused (touch) |
143
- | Publish | Top toolbar → **Publiser** |
144
- | Exit | Top toolbar → **Avslutt** (removes `?setto=edit`) |
157
+ | Publish | FAB → **Publiser** (visible once you have drafts) |
145
158
 
146
- A fixed toolbar sits at the top of the viewport. When you click a section or block, a compact colour toolbar appears above it, centred and sized to its contents. Click again or press Escape to dismiss.
159
+ When you click a section or block, a compact colour toolbar appears above it. Click again or press Escape to dismiss.
147
160
 
148
161
  ---
149
162
 
@@ -308,7 +321,7 @@ Keeping this in the repo means content shape lives with the code that defines it
308
321
 
309
322
  ## Publish flow
310
323
 
311
- 1. Editor clicks **Publiser** in the top toolbar.
324
+ 1. Editor clicks **Publiser** in the floating FAB.
312
325
  2. Client serialises changed locale bundles + `sections.json` (if theme drafts exist).
313
326
  3. `POST /sites/:siteId/publish` with `{ files: [{ path, content }] }`.
314
327
  4. setto-server validates paths against `content_paths`, commits to GitHub.
@@ -322,7 +335,7 @@ Drafts are cleared after a successful publish.
322
335
 
323
336
  Route: `/setto/*`
324
337
 
325
- Provides Supabase email/password login (invite-only — no self-service sign-up), password reset, and a dashboard with a link to start editing (`/?setto=edit`). Invite links from Supabase land on `/setto` to set a password. Does not render the inline editor itself.
338
+ Provides Supabase email/password login (invite-only — no self-service sign-up), password reset, and a dashboard. After sign-in the dashboard redirects to the site home, where edit mode auto-activates. Invite links from Supabase land on `/setto` to set a password. Does not render the inline editor itself.
326
339
 
327
340
  ---
328
341
 
@@ -2,10 +2,9 @@
2
2
  * Drop-in admin SPA. Mount under a route like `<Route path="/setto/*" .../>`.
3
3
  *
4
4
  * Behaviour after login:
5
- * - Redirects to `/?setto=edit` on the site home (edit mode active).
5
+ * - Redirects to the site home `/`. Edit mode activates automatically there
6
+ * via `SettoProvider` when the user has access to `config.siteId`.
6
7
  * - Shows the dashboard only when the user lacks access to this site, or on
7
8
  * `/setto?deployment=…` for publish progress / history.
8
- *
9
- * Editing happens on the public site at `/?setto=edit`.
10
9
  */
11
10
  export declare function SettoAdminApp(): import("react/jsx-runtime").JSX.Element;
@@ -1,2 +1,4 @@
1
- /** Height of the fixed edit toolbar (px). */
2
- export declare const TOOLBAR_HEIGHT = 44;
1
+ /** Diameter of the collapsed FAB and height of the expanded pill (px). */
2
+ export declare const FAB_SIZE = 52;
3
+ /** Distance from the viewport edges to the FAB (px). */
4
+ export declare const FAB_INSET = 16;
@@ -1,5 +1,7 @@
1
1
  /**
2
- * Reserves space at the top of the viewport for the Setto edit toolbar.
3
- * The host site keeps full width only vertical offset is applied.
2
+ * Applies the document-level CSS for inline edit mode. The fixed top toolbar
3
+ * has been replaced with a floating FAB anchored bottom-right, so the host
4
+ * page no longer needs vertical padding — only edit-affordance hover styles
5
+ * and the portal layers above the page.
4
6
  */
5
7
  export declare function useSettoDocumentLayout(active: boolean): void;
@@ -5,8 +5,8 @@ interface EditModeShellProps {
5
5
  themeStore: ThemeStore | null;
6
6
  }
7
7
  /**
8
- * Edit mode: full-width page with a fixed toolbar at the top. Text is edited
9
- * inline via contentEditable on `<T>` — no sidebar.
8
+ * Edit mode: full-width page with a floating Setto FAB anchored bottom-right.
9
+ * Text is edited inline via contentEditable on `<T>` — no sidebar.
10
10
  */
11
11
  export declare function EditModeShell({ children, themeStore }: EditModeShellProps): import("react/jsx-runtime").JSX.Element;
12
12
  /** @deprecated Use EditModeShell */
@@ -0,0 +1,16 @@
1
+ import type { CSSProperties } from 'react';
2
+ interface SettoMarkProps {
3
+ size?: number;
4
+ /** Color of the "S" letter. Defaults to the Setto crema tone. */
5
+ letterColor?: string;
6
+ /** Color of the accent dot. Defaults to the Setto terracotta. */
7
+ dotColor?: string;
8
+ style?: CSSProperties;
9
+ }
10
+ /**
11
+ * Setto brand mark — the foreground of the setto-site favicon (italic "S" with
12
+ * a terracotta dot) on a transparent background. The FAB renders the dark
13
+ * Blekk background itself so the mark only carries the glyphs.
14
+ */
15
+ export declare function SettoMark({ size, letterColor, dotColor, style, }: SettoMarkProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -27,6 +27,8 @@ export declare class ThemeStore {
27
27
  set(sectionId: string, field: string, value: string): void;
28
28
  /** Replace baseline from GitHub without creating drafts. */
29
29
  loadBaseline(next: SectionsTheme): void;
30
+ /** Resets every draft back to its original value. */
31
+ revertAll(): void;
30
32
  commit(): void;
31
33
  size(): number;
32
34
  serialise(): SectionsTheme;
@@ -1,11 +1,15 @@
1
1
  /**
2
2
  * Base path where the Setto login + dashboard SPA is mounted on the host site.
3
- * Editors reach edit mode by visiting `sitenavn.no/setto`. The host app must
4
- * mount `<SettoAdminApp>` at this path (e.g. `<Route path="/setto/*" …>`).
3
+ * Editors reach the dashboard by visiting `sitenavn.no/setto`. The host app
4
+ * must mount `<SettoAdminApp>` at this path (e.g. `<Route path="/setto/*" …>`).
5
5
  */
6
6
  export declare const SETTO_BASE = "/setto";
7
- /** Site home with inline edit mode active. */
8
- export declare function editModeUrl(pathname?: string): string;
7
+ /**
8
+ * Site home URL used after sign-in. Edit mode itself is now driven by the
9
+ * Supabase session + site access — no URL flag required — so this just sends
10
+ * the editor to the public site root.
11
+ */
12
+ export declare function siteHomeUrl(pathname?: string): string;
9
13
  export declare function isAdminRoute(): boolean;
10
14
  /** Setto deployment progress panel (`/setto?deployment=…`). */
11
15
  export declare function isAdminDeploymentView(): boolean;
@@ -0,0 +1,16 @@
1
+ import type { Session, SupabaseClient } from '@supabase/supabase-js';
2
+ import type { SiteRow } from '../types';
3
+ export interface SiteAccessState {
4
+ /** All sites the current user can access. `null` until loaded. */
5
+ sites: SiteRow[] | null;
6
+ /** The site matching `siteId`, or `null` if the user has no access. */
7
+ currentSite: SiteRow | null;
8
+ loading: boolean;
9
+ error: string | null;
10
+ }
11
+ /**
12
+ * Loads the sites table for the current Supabase session and resolves whether
13
+ * the user has access to `siteId`. Returns a stable shape across loading,
14
+ * success, and failure so callers can read `currentSite` directly.
15
+ */
16
+ export declare function useSiteAccess(supabase: SupabaseClient, session: Session | null, siteId: string): SiteAccessState;
@@ -4,7 +4,7 @@ import { type SettoApi } from './lib/api';
4
4
  import { I18nStore } from './lib/i18n-store';
5
5
  import { AssetStore } from './lib/asset-store';
6
6
  import { ThemeStore } from './lib/theme-store';
7
- import type { SettoConfig } from './types';
7
+ import type { SettoConfig, SiteRow } from './types';
8
8
  interface SettoContextValue {
9
9
  config: SettoConfig;
10
10
  supabase: SupabaseClient;
@@ -12,8 +12,12 @@ interface SettoContextValue {
12
12
  session: Session | null;
13
13
  /** True while the initial auth state is being read from local storage. */
14
14
  authLoading: boolean;
15
- /** True when inline edit mode is active (`?setto=edit` + session). */
15
+ /** True when inline edit mode is active (session + site access). */
16
16
  editMode: boolean;
17
+ /** Site row matching `config.siteId`, or `null` if the user has no access. */
18
+ currentSite: SiteRow | null;
19
+ /** All sites the user can access (loaded after sign-in). */
20
+ sites: SiteRow[] | null;
17
21
  /**
18
22
  * Lazily created when i18next is initialised. Components that need it
19
23
  * should `if (!store) return null;` until then.
@@ -32,10 +36,11 @@ export interface SettoProviderProps {
32
36
  *
33
37
  * Edit mode is on when ALL of:
34
38
  * 1. There is an authenticated Supabase session.
35
- * 2. The URL contains `?setto=edit`.
39
+ * 2. The user has access to `config.siteId` in the Setto sites table.
40
+ * 3. The current path is not the `/setto` admin SPA.
36
41
  *
37
- * The /setto dashboard does not activate edit mode use "Begynn å redigere"
38
- * which navigates to `/?setto=edit`.
42
+ * Once an editor signs in via `/setto`, returning visits auto-enter edit mode
43
+ * for as long as the Supabase session persists.
39
44
  */
40
45
  export declare function SettoProvider({ config, children }: SettoProviderProps): import("react/jsx-runtime").JSX.Element;
41
46
  export declare function useSetto(): SettoContextValue;