@book.dev/ui 1.60.0 → 1.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/{EmojiGrid-xK5mPJPo.js → EmojiGrid-THzuvC4K.js} +75 -84
  2. package/dist/blockeditor/BlockEditor.d.ts +16 -0
  3. package/dist/blockeditor/EmojiMenu.d.ts +17 -0
  4. package/dist/blockeditor/__tests__/EmojiMenu.test.d.ts +1 -0
  5. package/dist/blockeditor/__tests__/readOnly.test.d.ts +1 -0
  6. package/dist/blockeditor/__tests__/titleHandoff.test.d.ts +1 -0
  7. package/dist/blockeditor/__tests__/triggerMenus.test.d.ts +1 -0
  8. package/dist/blockeditor/kit/KitFrame.d.ts +5 -0
  9. package/dist/blockeditor/kit/lock.d.ts +14 -0
  10. package/dist/components/LastEditedBy.d.ts +11 -0
  11. package/dist/components/OnboardingNudge.d.ts +11 -0
  12. package/dist/components/ShareDialog.d.ts +25 -0
  13. package/dist/components/__tests__/lastEditedBy.test.d.ts +1 -0
  14. package/dist/components/__tests__/membersSettings.test.d.ts +1 -0
  15. package/dist/components/__tests__/sharingSettings.test.d.ts +1 -0
  16. package/dist/components/settings/AccountSettings.d.ts +4 -0
  17. package/dist/components/settings/AccountSwitcher.d.ts +12 -0
  18. package/dist/components/settings/MembersSettings.d.ts +1 -0
  19. package/dist/components/settings/SharingSettings.d.ts +9 -0
  20. package/dist/emoji-Bmft6RPl.js +11 -0
  21. package/dist/{format-CLQoRoYP.js → format-D9FO3jG9.js} +114 -114
  22. package/dist/i18n/messages/en.d.ts +169 -0
  23. package/dist/index.js +7238 -5976
  24. package/dist/lib/__tests__/useCanWrite.test.d.ts +1 -0
  25. package/dist/lib/hud.d.ts +2 -2
  26. package/dist/lib/sidebarStyles.d.ts +7 -0
  27. package/dist/lib/useCanWrite.d.ts +45 -0
  28. package/dist/{lucideIcons-B6pmC-WQ.js → lucideIcons-Dv5yzk2L.js} +1916 -1016
  29. package/dist/providers/AccountProvider.d.ts +47 -7
  30. package/dist/providers/ForwardingProvider.d.ts +19 -0
  31. package/dist/providers/PlatformLibraryProvider.d.ts +20 -0
  32. package/dist/providers/__tests__/AccountProvider.test.d.ts +1 -0
  33. package/dist/providers/__tests__/forwardingAudience.test.d.ts +9 -0
  34. package/dist/providers/forwardingAudience.d.ts +165 -0
  35. package/dist/screens/pageChrome.d.ts +11 -0
  36. package/dist/style.css +1 -1
  37. package/dist/{toHtml-BoPr8Ce4.js → toHtml-DeWpCU0o.js} +2 -2
  38. package/package.json +7 -2
@@ -1,18 +1,9 @@
1
- import { J as e, Mt as t, i as n, n as r, t as i } from "./lucideIcons-B6pmC-WQ.js";
2
- import { useMemo as a, useRef as o, useState as s } from "react";
3
- import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
4
- import { search as d } from "node-emoji";
5
- //#region src/lib/emoji.ts
6
- function f(e, t = 8) {
7
- let n = e.trim().toLowerCase();
8
- return n ? d(n).slice().sort((e, t) => p(e.name, n) - p(t.name, n)).slice(0, t) : [];
9
- }
10
- function p(e, t) {
11
- return e === t ? 0 : e.startsWith(t) ? 1 : 2;
12
- }
13
- //#endregion
1
+ import { Mt as e, U as t, i as n, n as r, t as i } from "./lucideIcons-Dv5yzk2L.js";
2
+ import { t as a } from "./emoji-Bmft6RPl.js";
3
+ import { useMemo as o, useRef as s, useState as c } from "react";
4
+ import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
14
5
  //#region src/lib/emojiData.ts
15
- var m = [
6
+ var f = [
16
7
  {
17
8
  id: "smileys",
18
9
  icon: "😀",
@@ -54,152 +45,152 @@ var m = [
54
45
  emojis: /* @__PURE__ */ "❤️.🧡.💛.💚.💙.💜.🖤.🤍.🤎.💔.❣️.💕.💞.💓.💗.💖.💘.💝.💟.☮️.✝️.☪️.🕉️.☸️.✡️.🔯.🕎.☯️.☦️.⛎.♈.♉.♊.♋.♌.♍.♎.♏.♐.♑.♒.♓.🆔.⚛️.🉑.☢️.☣️.📴.✅.❌.❎.➕.➖.➗.✖️.♾️.‼️.⁉️.❓.❔.❕.❗.〰️.⚠️.🚸.🔱.⚜️.🔰.♻️.✳️.❇️.⭐.🌟.💫.⭕.🔴.🟠.🟡.🟢.🔵.🟣.🟤.⚫.⚪.🔺.🔻.🔸.🔹.🔶.🔷.🔳.🔲.▪️.▫️.◼️.◻️.🏁.🚩.🎌.🏴.🏳️.🏳️‍🌈.🔔.🔕.🎵.🎶.💬.💭".split(".")
55
46
  }
56
47
  ];
57
- Array.from(new Set(m.flatMap((e) => e.emojis)));
48
+ Array.from(new Set(f.flatMap((e) => e.emojis)));
58
49
  //#endregion
59
50
  //#region src/lib/iconRecents.ts
60
- var h = "openbook.iconRecents", g = 24;
61
- function _() {
51
+ var p = "openbook.iconRecents", m = 24;
52
+ function h() {
62
53
  if (typeof localStorage > "u") return [];
63
54
  try {
64
- let e = localStorage.getItem(h), t = e ? JSON.parse(e) : [];
65
- return Array.isArray(t) ? t.filter((e) => typeof e == "string").slice(0, g) : [];
55
+ let e = localStorage.getItem(p), t = e ? JSON.parse(e) : [];
56
+ return Array.isArray(t) ? t.filter((e) => typeof e == "string").slice(0, m) : [];
66
57
  } catch {
67
58
  return [];
68
59
  }
69
60
  }
70
- function v(e) {
61
+ function g(e) {
71
62
  if (!(typeof localStorage > "u" || !e)) try {
72
- let t = [e, ..._().filter((t) => t !== e)].slice(0, g);
73
- localStorage.setItem(h, JSON.stringify(t));
63
+ let t = [e, ...h().filter((t) => t !== e)].slice(0, m);
64
+ localStorage.setItem(p, JSON.stringify(t));
74
65
  } catch {}
75
66
  }
76
67
  //#endregion
77
68
  //#region src/components/EmojiGrid.tsx
78
- function y({ value: t, onPick: n }) {
79
- let { t: r } = e(), [i, a] = s(t?.startsWith("lucide:") ? "icon" : "emoji"), [c, d] = s(""), f = o(_()).current;
80
- return /* @__PURE__ */ u("div", {
69
+ function _({ value: e, onPick: n }) {
70
+ let { t: r } = t(), [i, a] = c(e?.startsWith("lucide:") ? "icon" : "emoji"), [o, l] = c(""), f = s(h()).current;
71
+ return /* @__PURE__ */ d("div", {
81
72
  className: "flex h-[372px] w-[332px] flex-col bg-popover text-popover-foreground",
82
73
  children: [
83
- /* @__PURE__ */ u("div", {
74
+ /* @__PURE__ */ d("div", {
84
75
  className: "flex items-center gap-1 p-2 pb-1.5",
85
- children: [/* @__PURE__ */ l(b, {
76
+ children: [/* @__PURE__ */ u(v, {
86
77
  active: i === "emoji",
87
78
  onClick: () => a("emoji"),
88
79
  label: r("emoji.tabEmoji")
89
- }), /* @__PURE__ */ l(b, {
80
+ }), /* @__PURE__ */ u(v, {
90
81
  active: i === "icon",
91
82
  onClick: () => a("icon"),
92
83
  label: r("emoji.tabIcons")
93
84
  })]
94
85
  }),
95
- /* @__PURE__ */ l("div", {
86
+ /* @__PURE__ */ u("div", {
96
87
  className: "px-2 pb-2",
97
- children: /* @__PURE__ */ l("input", {
88
+ children: /* @__PURE__ */ u("input", {
98
89
  autoFocus: !0,
99
- value: c,
100
- onChange: (e) => d(e.target.value),
90
+ value: o,
91
+ onChange: (e) => l(e.target.value),
101
92
  placeholder: r(i === "emoji" ? "emoji.searchEmoji" : "emoji.searchIcons"),
102
93
  className: "h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm outline-hidden placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring",
103
94
  "aria-label": r("emoji.search")
104
95
  })
105
96
  }),
106
- /* @__PURE__ */ l("div", {
97
+ /* @__PURE__ */ u("div", {
107
98
  className: "min-h-0 flex-1 overflow-y-auto px-2 pb-2",
108
- children: l(i === "emoji" ? w : E, {
109
- query: c,
99
+ children: u(i === "emoji" ? S : w, {
100
+ query: o,
110
101
  recents: f,
111
- current: t,
102
+ current: e,
112
103
  onPick: (e) => {
113
- v(e), n(e);
104
+ g(e), n(e);
114
105
  }
115
106
  })
116
107
  })
117
108
  ]
118
109
  });
119
110
  }
120
- function b({ active: e, onClick: n, label: r }) {
121
- return /* @__PURE__ */ l("button", {
111
+ function v({ active: t, onClick: n, label: r }) {
112
+ return /* @__PURE__ */ u("button", {
122
113
  type: "button",
123
114
  onClick: n,
124
- className: t("flex-1 cursor-pointer rounded-md px-2.5 py-1 text-sm font-medium transition-colors", e ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:bg-hover"),
115
+ className: e("flex-1 cursor-pointer rounded-md px-2.5 py-1 text-sm font-medium transition-colors", t ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:bg-hover"),
125
116
  children: r
126
117
  });
127
118
  }
128
- function x({ children: e }) {
129
- return /* @__PURE__ */ l("div", {
119
+ function y({ children: e }) {
120
+ return /* @__PURE__ */ u("div", {
130
121
  className: "sticky top-0 z-10 bg-popover px-1 pb-1 pt-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground/70",
131
122
  children: e
132
123
  });
133
124
  }
134
- var S = "grid grid-cols-8 gap-0.5";
135
- function C({ glyph: e, active: n, onPick: r }) {
136
- return /* @__PURE__ */ l("button", {
125
+ var b = "grid grid-cols-8 gap-0.5";
126
+ function x({ glyph: t, active: n, onPick: r }) {
127
+ return /* @__PURE__ */ u("button", {
137
128
  type: "button",
138
- onClick: () => r(e),
139
- className: t("flex h-9 w-9 items-center justify-center rounded-md text-[20px] leading-none transition-colors hover:bg-hover", n && "bg-accent ring-1 ring-primary"),
140
- title: e,
141
- children: e
129
+ onClick: () => r(t),
130
+ className: e("flex h-9 w-9 items-center justify-center rounded-md text-[20px] leading-none transition-colors hover:bg-hover", n && "bg-accent ring-1 ring-primary"),
131
+ title: t,
132
+ children: t
142
133
  });
143
134
  }
144
- function w({ query: t, recents: r, current: i, onPick: o }) {
145
- let { t: s } = e(), d = a(() => t.trim() ? f(t, 64).map((e) => e.emoji) : null, [t]), p = r.filter((e) => !e.startsWith(n));
146
- return d ? d.length === 0 ? /* @__PURE__ */ l(D, { text: s("emoji.noResults") }) : /* @__PURE__ */ l("div", {
147
- className: S,
148
- children: d.map((e, t) => /* @__PURE__ */ l(C, {
135
+ function S({ query: e, recents: r, current: i, onPick: s }) {
136
+ let { t: c } = t(), p = o(() => e.trim() ? a(e, 64).map((e) => e.emoji) : null, [e]), m = r.filter((e) => !e.startsWith(n));
137
+ return p ? p.length === 0 ? /* @__PURE__ */ u(T, { text: c("emoji.noResults") }) : /* @__PURE__ */ u("div", {
138
+ className: b,
139
+ children: p.map((e, t) => /* @__PURE__ */ u(x, {
149
140
  glyph: e,
150
141
  active: e === i,
151
- onPick: o
142
+ onPick: s
152
143
  }, `${e}-${t}`))
153
- }) : /* @__PURE__ */ u(c, { children: [p.length > 0 && /* @__PURE__ */ u("section", { children: [/* @__PURE__ */ l(x, { children: s("emoji.recent") }), /* @__PURE__ */ l("div", {
154
- className: S,
155
- children: p.map((e, t) => /* @__PURE__ */ l(C, {
144
+ }) : /* @__PURE__ */ d(l, { children: [m.length > 0 && /* @__PURE__ */ d("section", { children: [/* @__PURE__ */ u(y, { children: c("emoji.recent") }), /* @__PURE__ */ u("div", {
145
+ className: b,
146
+ children: m.map((e, t) => /* @__PURE__ */ u(x, {
156
147
  glyph: e,
157
148
  active: e === i,
158
- onPick: o
149
+ onPick: s
159
150
  }, `r-${e}-${t}`))
160
- })] }), m.map((e) => /* @__PURE__ */ u("section", { children: [/* @__PURE__ */ l(x, { children: s(`emoji.cat.${e.id}`) }), /* @__PURE__ */ l("div", {
161
- className: S,
162
- children: e.emojis.map((t, n) => /* @__PURE__ */ l(C, {
151
+ })] }), f.map((e) => /* @__PURE__ */ d("section", { children: [/* @__PURE__ */ u(y, { children: c(`emoji.cat.${e.id}`) }), /* @__PURE__ */ u("div", {
152
+ className: b,
153
+ children: e.emojis.map((t, n) => /* @__PURE__ */ u(x, {
163
154
  glyph: t,
164
155
  active: t === i,
165
- onPick: o
156
+ onPick: s
166
157
  }, `${e.id}-${t}-${n}`))
167
158
  })] }, e.id))] });
168
159
  }
169
- function T({ name: e, active: r, onPick: a }) {
170
- let o = i[e];
171
- return o ? /* @__PURE__ */ l("button", {
160
+ function C({ name: t, active: r, onPick: a }) {
161
+ let o = i[t];
162
+ return o ? /* @__PURE__ */ u("button", {
172
163
  type: "button",
173
- onClick: () => a(`${n}${e}`),
174
- className: t("flex h-9 w-9 items-center justify-center rounded-md text-foreground transition-colors hover:bg-hover", r && "bg-accent ring-1 ring-primary"),
175
- title: e,
176
- "aria-label": e,
177
- children: /* @__PURE__ */ l(o, { className: "h-[18px] w-[18px]" })
164
+ onClick: () => a(`${n}${t}`),
165
+ className: e("flex h-9 w-9 items-center justify-center rounded-md text-foreground transition-colors hover:bg-hover", r && "bg-accent ring-1 ring-primary"),
166
+ title: t,
167
+ "aria-label": t,
168
+ children: /* @__PURE__ */ u(o, { className: "h-[18px] w-[18px]" })
178
169
  }) : null;
179
170
  }
180
- function E({ query: t, recents: o, current: s, onPick: d }) {
181
- let { t: f } = e(), p = t.trim().toLowerCase(), m = a(() => p ? r.filter((e) => e.toLowerCase().includes(p)) : r, [p]), h = s?.startsWith("lucide:") ? s.slice(n.length) : void 0, g = o.filter((e) => e.startsWith(n)).map((e) => e.slice(n.length)).filter((e) => i[e]);
182
- return m.length === 0 ? /* @__PURE__ */ l(D, { text: f("emoji.noResults") }) : /* @__PURE__ */ u(c, { children: [!p && g.length > 0 && /* @__PURE__ */ u("section", { children: [/* @__PURE__ */ l(x, { children: f("emoji.recent") }), /* @__PURE__ */ l("div", {
183
- className: S,
184
- children: g.map((e, t) => /* @__PURE__ */ l(T, {
171
+ function w({ query: e, recents: a, current: s, onPick: c }) {
172
+ let { t: f } = t(), p = e.trim().toLowerCase(), m = o(() => p ? r.filter((e) => e.toLowerCase().includes(p)) : r, [p]), h = s?.startsWith("lucide:") ? s.slice(n.length) : void 0, g = a.filter((e) => e.startsWith(n)).map((e) => e.slice(n.length)).filter((e) => i[e]);
173
+ return m.length === 0 ? /* @__PURE__ */ u(T, { text: f("emoji.noResults") }) : /* @__PURE__ */ d(l, { children: [!p && g.length > 0 && /* @__PURE__ */ d("section", { children: [/* @__PURE__ */ u(y, { children: f("emoji.recent") }), /* @__PURE__ */ u("div", {
174
+ className: b,
175
+ children: g.map((e, t) => /* @__PURE__ */ u(C, {
185
176
  name: e,
186
177
  active: e === h,
187
- onPick: d
178
+ onPick: c
188
179
  }, `r-${e}-${t}`))
189
- })] }), /* @__PURE__ */ u("section", { children: [!p && /* @__PURE__ */ l(x, { children: f("emoji.tabIcons") }), /* @__PURE__ */ l("div", {
190
- className: S,
191
- children: m.map((e) => /* @__PURE__ */ l(T, {
180
+ })] }), /* @__PURE__ */ d("section", { children: [!p && /* @__PURE__ */ u(y, { children: f("emoji.tabIcons") }), /* @__PURE__ */ u("div", {
181
+ className: b,
182
+ children: m.map((e) => /* @__PURE__ */ u(C, {
192
183
  name: e,
193
184
  active: e === h,
194
- onPick: d
185
+ onPick: c
195
186
  }, e))
196
187
  })] })] });
197
188
  }
198
- function D({ text: e }) {
199
- return /* @__PURE__ */ l("div", {
189
+ function T({ text: e }) {
190
+ return /* @__PURE__ */ u("div", {
200
191
  className: "flex h-24 items-center justify-center text-sm text-muted-foreground",
201
192
  children: e
202
193
  });
203
194
  }
204
195
  //#endregion
205
- export { y as default };
196
+ export { _ as default };
@@ -14,6 +14,7 @@ import type { InlineAttrs } from './model';
14
14
  export interface EditorUI {
15
15
  slash: SlashState;
16
16
  mention: SlashState;
17
+ emoji: SlashState;
17
18
  spellcheck: boolean;
18
19
  openSlash(blockId: string, anchorOffset: number): void;
19
20
  updateSlash(caret: number): void;
@@ -23,8 +24,19 @@ export interface EditorUI {
23
24
  updateMention(caret: number): void;
24
25
  closeMention(): void;
25
26
  mentionKey(key: string): void;
27
+ openEmoji(blockId: string, anchorOffset: number): void;
28
+ updateEmoji(caret: number): void;
29
+ closeEmoji(): void;
30
+ emojiKey(key: string): void;
26
31
  toggleFormat(key: keyof InlineAttrs, value?: string): void;
27
32
  scheduleToolbar(): void;
33
+ /** Leave the editor for the page title above (↑/←/Backspace at the very top). */
34
+ leaveToTitle?(): void;
35
+ }
36
+ /** Imperative handle the host uses to hand the caret into the editor. */
37
+ export interface BlockEditorHandle {
38
+ /** Focus the first text block at its start, creating a paragraph if empty. */
39
+ focusStart(): void;
28
40
  }
29
41
  export type DropRegion = 'above' | 'below' | 'left' | 'right';
30
42
  interface DragState {
@@ -47,6 +59,10 @@ export declare const BlockEditor: React.FC<{
47
59
  spellcheck?: boolean;
48
60
  /** The page hosting this editor — powers the "New page/database" commands. */
49
61
  pageId?: string;
62
+ /** Imperative handle so the title can hand the caret to the first block. */
63
+ focusRef?: React.Ref<BlockEditorHandle>;
64
+ /** Leave the editor for the title above (caret at the top of the document). */
65
+ onLeaveToTitle?: () => void;
50
66
  }>;
51
67
  export interface RowShared {
52
68
  editor: BlockEditorController;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import type { SlashState } from './SlashMenu';
3
+ import type { BlockEditorController } from './useBlockEditor';
4
+ /**
5
+ * The ":" emoji menu: like the slash and mention menus but for emoji. Trigger
6
+ * detection, query tracking and key forwarding mirror those menus; this only
7
+ * differs in its items (offline `searchEmojis`) and how a pick is inserted —
8
+ * the typed ":query" is removed and the chosen glyph dropped in its place.
9
+ */
10
+ export declare const EmojiMenu: React.FC<{
11
+ state: SlashState;
12
+ editor: BlockEditorController;
13
+ anchorEl: HTMLElement | null;
14
+ onClose: () => void;
15
+ /** Insert the chosen glyph (plain text) at the offset where ":" was typed. */
16
+ onInsertText: (blockId: string, anchorOffset: number, text: string) => void;
17
+ }>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -44,6 +44,11 @@ export declare const ConfigToggle: React.FC<{
44
44
  * so it looks like the text it replaces; falls back to a static span when the
45
45
  * editor is read-only (or the block's group is locked). `stopPropagation` keeps
46
46
  * keystrokes out of the surrounding block editor's shortcuts.
47
+ *
48
+ * On a read-only page the widget's *control* revives (interactive exemption)
49
+ * with a `liveEditor`, so `readOnly` arrives `false` here — but the label is
50
+ * chrome, not a control, and must stay frozen. {@link useKitPageLock} re-freezes
51
+ * it independently of the control's editor.
47
52
  */
48
53
  export declare const KitInlineText: React.FC<{
49
54
  value: string;
@@ -10,3 +10,17 @@ export interface KitLockState {
10
10
  export declare const KitLockContext: import("react").Context<KitLockState>;
11
11
  /** Whether the nearest enclosing group is locked. */
12
12
  export declare const useKitLock: () => boolean;
13
+ /**
14
+ * Whether the whole page is read-only (a viewer who can't write). Set ONCE at
15
+ * the editor root and never re-provided by groups, so it survives nesting —
16
+ * unlike {@link KitLockContext}, which a locked group flips on for its subtree.
17
+ *
18
+ * Interactive widgets revive under a page lock (their control gets a `liveEditor`
19
+ * with `readOnly:false`), which would otherwise make their kit chrome — the
20
+ * inline label / description — editable again. The {@link KitInlineText} chrome
21
+ * consults this so the control stays live while the label/config text stays
22
+ * frozen for the reader.
23
+ */
24
+ export declare const KitPageLockContext: import("react").Context<boolean>;
25
+ /** Whether the whole page is read-only (viewer / can't-write). */
26
+ export declare const useKitPageLock: () => boolean;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A subtle "Edited by X · 2h" indicator in the page header cluster (OB-165),
3
+ * read from the change provenance log so the *who* and *when* of the last edit
4
+ * are visible, not just stored. Renders nothing when the server has no edit log
5
+ * (an older build) or the page has no recorded edits yet. Refreshes when the
6
+ * page is saved so it reflects the latest editor (including you).
7
+ */
8
+ export declare function LastEditedBy({ pageId }: {
9
+ pageId: string;
10
+ }): import("react/jsx-runtime").JSX.Element | null;
11
+ export default LastEditedBy;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A tasteful, dismissible "sign up free to publish" prompt for the genuinely
3
+ * unauthenticated user — the on-ramp from local-first into a first publish
4
+ * (OB-206). It sits at the foot of the sidebar and only ever appears when no
5
+ * account is connected; signing in (or dismissing it) retires it for good. We
6
+ * never force it: local-first stays the default, this is just the door.
7
+ *
8
+ * The CTA opens Settings → Account, where the actual sign-in / add-account flow
9
+ * lives, rather than firing the OAuth popup straight from the sidebar.
10
+ */
11
+ export default function OnboardingNudge(): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,25 @@
1
+ import { type InstanceInfo } from '@book.dev/sdk';
2
+ /**
3
+ * Does this principal manage sharing instance-wide? The loopback owner, an
4
+ * instance admin, and the claimed owner all do; on a still-unclaimed instance the
5
+ * legacy guest gate governs (anyone who may write). This is the coarse gate for
6
+ * the Share *entry point* — the per-page routes still enforce write server-side,
7
+ * and the dialog degrades to an error state if a specific page load is refused.
8
+ */
9
+ export declare function canManageSharing(info: InstanceInfo): boolean;
10
+ /**
11
+ * Whether the current user may manage page sharing (gates the Share control).
12
+ * `null` while the one-shot `/api/instance` lookup is in flight; `false` if the
13
+ * server predates multi-user (no endpoint) so the control simply stays hidden.
14
+ */
15
+ export declare function useCanManageSharing(): boolean | null;
16
+ /**
17
+ * The per-page Share dialog (OB-203). The page owner / admin sets the page's
18
+ * audience-scope visibility and grants individual people read/edit access by
19
+ * email or handle, all against the OB-191 per-page API (`setPageVisibility`,
20
+ * `sharePage`/`listPageAcl`/`unsharePage`). Rendered from the page-actions
21
+ * cluster; only mounted when {@link useCanManageSharing} clears the user.
22
+ */
23
+ export default function ShareDialog({ pageId }: {
24
+ pageId: string;
25
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export {};
@@ -2,5 +2,9 @@
2
2
  * Account & sync: connect this device to account.book.pub (the deep-link OAuth
3
3
  * flow) and mirror preferences + the workspace list there. The data server is
4
4
  * untouched — only settings sync through the account service.
5
+ *
6
+ * With no account connected this is the sign-in surface; once at least one is
7
+ * connected it hands off to {@link AccountSwitcher} — the multi-account list
8
+ * (switch / add / remove / sign out) introduced in OB-206.
5
9
  */
6
10
  export default function AccountSettings(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The multi-account switcher (OB-206): lists every connected account, marks the
3
+ * active one with a live status, and lets the user switch between them, add
4
+ * another, remove one, or sign the active one out — all over the `useAccount()`
5
+ * actions (the provider owns the logic; this is the surface). The single active
6
+ * account's sync details and dashboard link sit below the list.
7
+ *
8
+ * Per Devon's OB-194 note the dormant accounts read as "Signed in" (available),
9
+ * never "syncing": only the active account talks to the server, so only it shows
10
+ * a live status.
11
+ */
12
+ export default function AccountSwitcher(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export default function MembersSettings(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Multi-user access policy (OB-165): who can read/edit this shared workspace
3
+ * without signing in, plus who the server currently sees *you* as. Reads the
4
+ * instance policy from the data server and lets the owner change the guest gate.
5
+ *
6
+ * Hidden when the server doesn't expose `/api/instance` (an older build), so it
7
+ * degrades cleanly against a server that predates multi-user support.
8
+ */
9
+ export declare function SharingSection(): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,11 @@
1
+ import { search as e } from "node-emoji";
2
+ //#region src/lib/emoji.ts
3
+ function t(t, r = 8) {
4
+ let i = t.trim().toLowerCase();
5
+ return i ? e(i).slice().sort((e, t) => n(e.name, i) - n(t.name, i)).slice(0, r) : [];
6
+ }
7
+ function n(e, t) {
8
+ return e === t ? 0 : e.startsWith(t) ? 1 : 2;
9
+ }
10
+ //#endregion
11
+ export { t };