@tangle-network/agent-app 0.60.183 → 0.60.185
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/agent-profiles/d1.js +3 -3
- package/dist/agent-profiles/index.js +3 -3
- package/dist/assistant/index.js +9 -9
- package/dist/channels/index.js +1 -1
- package/dist/chat-react/index.js +4 -4
- package/dist/chat-routes/index.js +4 -4
- package/dist/{chunk-YXLN4PXK.js → chunk-2RCPPOQ4.js} +2 -2
- package/dist/{chunk-YESFGM56.js → chunk-5IS3GCMR.js} +3 -3
- package/dist/{chunk-3RHAF2P5.js → chunk-A3CUVYRZ.js} +3 -3
- package/dist/{chunk-LN5RBVGP.js → chunk-AVP5SNIG.js} +4 -4
- package/dist/{chunk-LN5RBVGP.js.map → chunk-AVP5SNIG.js.map} +1 -1
- package/dist/{chunk-7BYY7QKC.js → chunk-E65YV7VV.js} +5 -5
- package/dist/{chunk-7BYY7QKC.js.map → chunk-E65YV7VV.js.map} +1 -1
- package/dist/chunk-EZFBGW23.js +179 -0
- package/dist/chunk-EZFBGW23.js.map +1 -0
- package/dist/{chunk-UDLA3NGY.js → chunk-GNBBY3KN.js} +3 -3
- package/dist/{chunk-KOTNMTHV.js → chunk-HCFVEJQL.js} +3 -3
- package/dist/{chunk-YGBEGBJY.js → chunk-HEPEGY6Q.js} +2 -2
- package/dist/{chunk-AJZ3EJB5.js → chunk-IJAXOQJU.js} +2 -2
- package/dist/{chunk-EQRVOMMS.js → chunk-KMIDRUUH.js} +2 -2
- package/dist/{chunk-BBHVV37T.js → chunk-MSAGTFN4.js} +4 -4
- package/dist/chunk-OBKVNZPH.js +70 -0
- package/dist/chunk-OBKVNZPH.js.map +1 -0
- package/dist/{chunk-75ZIWV4U.js → chunk-QAF7ZTO3.js} +4 -4
- package/dist/{chunk-TJ4DYKF2.js → chunk-XG6FODYO.js} +3 -2
- package/dist/chunk-XG6FODYO.js.map +1 -0
- package/dist/{chunk-6O2NQ7WD.js → chunk-Y2SOQOJE.js} +3 -3
- package/dist/integrations-react/index.js +1 -1
- package/dist/{mention-editor-ZJAOUDEE.js → mention-editor-NOS5R3AU.js} +2 -2
- package/dist/preset-cloudflare/index.js +2 -2
- package/dist/sandbox/index.js +4 -4
- package/dist/settings-react/confirm-action.d.ts +51 -0
- package/dist/settings-react/delete-workspace-section.d.ts +31 -0
- package/dist/settings-react/export-section.d.ts +26 -0
- package/dist/settings-react/index.d.ts +20 -0
- package/dist/settings-react/index.js +1438 -0
- package/dist/settings-react/index.js.map +1 -0
- package/dist/settings-react/members-section.d.ts +66 -0
- package/dist/settings-react/save-status.d.ts +24 -0
- package/dist/settings-react/setting-outcome.d.ts +33 -0
- package/dist/settings-react/settings-layout.d.ts +71 -0
- package/dist/settings-react/settings-save-bar.d.ts +26 -0
- package/dist/settings-react/timezone-picker.d.ts +17 -0
- package/dist/settings-react/use-setting-field.d.ts +70 -0
- package/dist/stories/settings/settings-backend.d.ts +66 -0
- package/dist/stories/settings/settings-demo.d.ts +16 -0
- package/dist/stories/settings/team-panels-demo.d.ts +21 -0
- package/dist/studio-react/index.js +2 -2
- package/dist/teams-react/components/InviteForm.d.ts +2 -2
- package/dist/teams-react/components/MembersPanel.d.ts +9 -5
- package/dist/teams-react/components/RoleSelect.d.ts +10 -8
- package/dist/teams-react/components/invitation-detail.d.ts +8 -0
- package/dist/teams-react/components/member-access.d.ts +17 -0
- package/dist/teams-react/contracts.d.ts +2 -0
- package/dist/teams-react/index.js +119 -242
- package/dist/teams-react/index.js.map +1 -1
- package/dist/theme/tokens.css +43 -2
- package/dist/tools/index.js +4 -4
- package/dist/web-react/agent-profile-editor.js +4 -4
- package/dist/web-react/async/index.js +1 -1
- package/dist/web-react/controls.d.ts +4 -5
- package/dist/web-react/index.js +12 -12
- package/dist/work-product/index.js +2 -2
- package/dist/work-product-react/index.js +3 -3
- package/dist/work-product-react/index.js.map +1 -1
- package/dist/workspace-export/react.d.ts +26 -0
- package/dist/workspace-export/react.js +6 -169
- package/dist/workspace-export/react.js.map +1 -1
- package/dist/workspace-react/index.js +3 -3
- package/package.json +11 -6
- package/dist/chunk-TJ4DYKF2.js.map +0 -1
- /package/dist/{chunk-YXLN4PXK.js.map → chunk-2RCPPOQ4.js.map} +0 -0
- /package/dist/{chunk-YESFGM56.js.map → chunk-5IS3GCMR.js.map} +0 -0
- /package/dist/{chunk-3RHAF2P5.js.map → chunk-A3CUVYRZ.js.map} +0 -0
- /package/dist/{chunk-UDLA3NGY.js.map → chunk-GNBBY3KN.js.map} +0 -0
- /package/dist/{chunk-KOTNMTHV.js.map → chunk-HCFVEJQL.js.map} +0 -0
- /package/dist/{chunk-YGBEGBJY.js.map → chunk-HEPEGY6Q.js.map} +0 -0
- /package/dist/{chunk-AJZ3EJB5.js.map → chunk-IJAXOQJU.js.map} +0 -0
- /package/dist/{chunk-EQRVOMMS.js.map → chunk-KMIDRUUH.js.map} +0 -0
- /package/dist/{chunk-BBHVV37T.js.map → chunk-MSAGTFN4.js.map} +0 -0
- /package/dist/{chunk-75ZIWV4U.js.map → chunk-QAF7ZTO3.js.map} +0 -0
- /package/dist/{chunk-6O2NQ7WD.js.map → chunk-Y2SOQOJE.js.map} +0 -0
- /package/dist/{mention-editor-ZJAOUDEE.js.map → mention-editor-NOS5R3AU.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/settings-react/settings-layout.tsx","../../src/settings-react/save-status.tsx","../../src/settings-react/use-setting-field.tsx","../../src/settings-react/setting-outcome.ts","../../src/settings-react/confirm-action.tsx","../../src/settings-react/settings-save-bar.tsx","../../src/settings-react/members-section.tsx","../../src/settings-react/export-section.tsx","../../src/settings-react/delete-workspace-section.tsx","../../src/settings-react/timezone-picker.tsx"],"sourcesContent":["/**\n * The settings page: a `PageHeader`, one link per section and the current\n * section's cards, in `PageShell measure=\"settings\"`. Each section is its own\n * URL, so a product routes `/settings/<section>` and loads only that\n * section's data. The frame answers to the content area's width: from 60rem\n * the links are a sticky 13rem column beside a 48rem column of cards, and\n * below that a sticky 56px tab strip whose current tab is kept in view.\n *\n * Navigation responds at once: the clicked link becomes current before the\n * route resolves, nothing dims, and a thin progress line shows only after the\n * pending delay. A `#hash` scrolls its section (or row) into view and focuses\n * its heading.\n */\n\nimport { Button, PageHeader, PageShell } from '@tangle-network/ui/primitives'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type AnchorHTMLAttributes,\n type ComponentType,\n type MouseEvent,\n type ReactNode,\n type RefObject,\n} from 'react'\nimport { useBrowserLayoutEffect } from '../web-react/controls'\nimport { PENDING_DELAY_MS } from './save-status'\n\n/** An icon component such as a `lucide-react` icon. It shows only in the column. */\nexport type SettingsIcon = ComponentType<{ className?: string; 'aria-hidden'?: boolean | 'true' | 'false' }>\n\nexport interface SettingsNavSection {\n id: string\n label: string\n /** The section's own URL. */\n href: string\n icon?: SettingsIcon\n /** Leaves the link out, for a section the viewer's role cannot open. */\n hidden?: boolean\n /** A short count after the label, such as the number of members. */\n badge?: ReactNode\n}\n\n/** What a `LinkComponent` receives: render an anchor (or a router link) with these props. */\nexport interface SettingsLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n href: string\n children?: ReactNode\n}\n\nexport type SettingsLinkComponent = ComponentType<SettingsLinkProps>\n\nexport interface SettingsNavProps {\n sections: SettingsNavSection[]\n /** The section the route rendered. */\n activeId: string\n /**\n * Renders each link: pass the router's link (with prefetch off where the\n * edge refuses prefetch requests). Defaults to a plain anchor.\n */\n LinkComponent?: SettingsLinkComponent\n /** Called once per section when a pointer rests on or focus reaches its link: warm its route module here. */\n onIntent?: (section: SettingsNavSection) => void\n}\n\nexport interface SettingsLayoutProps extends SettingsNavProps {\n /** The page title, such as \"Project settings\". */\n title: ReactNode\n /** One line under the title, such as the workspace name. */\n description?: ReactNode\n /** A second line under the description, such as \"Setup in progress · Finish setup\". */\n meta?: ReactNode\n /** The current section's `SettingsSection` cards. */\n children: ReactNode\n /** The navigation landmark's name. */\n navigationLabel?: string\n /** Bounds the frame the layout answers to, such as `mx-auto max-w-5xl`. */\n className?: string\n}\n\n/** The default `LinkComponent`: a plain anchor, so a click is a document load. */\nexport function PlainLink({ href, children, ...rest }: SettingsLinkProps) {\n return <a href={href} {...rest}>{children}</a>\n}\n\nfunction isPlainClick(event: MouseEvent<HTMLAnchorElement>): boolean {\n return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.defaultPrevented\n}\n\n/** The section a click made current before its route resolved; cleared when the route lands or history moves. */\nfunction useOptimisticSection(activeId: string) {\n const [pendingId, setPendingId] = useState<string | null>(null)\n useEffect(() => setPendingId(null), [activeId])\n useEffect(() => {\n const clear = () => setPendingId(null)\n window.addEventListener('popstate', clear)\n return () => window.removeEventListener('popstate', clear)\n }, [])\n return { currentId: pendingId ?? activeId, pending: pendingId !== null && pendingId !== activeId, setPendingId }\n}\n\n/** The edge panel a chevron sits on: a 44px touch target that takes no room in the strip. */\nconst CUE_PANEL = 44\nconst STRIP_GAP = 4\n\n/** Chevrons on the strip's edges while links lie past them. They page the strip; touch and trackpads scroll it natively. */\nfunction useStripOverflow(listRef: RefObject<HTMLUListElement | null>) {\n const [overflow, setOverflow] = useState({ start: false, end: false })\n const strip = useCallback(() => listRef.current?.parentElement ?? null, [listRef])\n // Measured before paint, so a strip that overflows shows its chevrons on the first frame.\n useBrowserLayoutEffect(() => {\n const nav = strip()\n if (!nav) return\n const measure = () => {\n // The column never scrolls sideways, so it never shows a chevron.\n const scrolls = getComputedStyle(nav).overflowX !== 'visible' && nav.scrollWidth > nav.clientWidth + 1\n const start = scrolls && nav.scrollLeft > 1\n const end = scrolls && nav.scrollLeft + nav.clientWidth < nav.scrollWidth - 1\n setOverflow((prev) => (prev.start === start && prev.end === end ? prev : { start, end }))\n }\n measure()\n nav.addEventListener('scroll', measure, { passive: true })\n const resize = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure)\n resize?.observe(nav)\n if (listRef.current) resize?.observe(listRef.current)\n return () => {\n nav.removeEventListener('scroll', measure)\n resize?.disconnect()\n }\n }, [listRef, strip])\n const page = (direction: 1 | -1) => {\n const nav = strip()\n if (!nav) return\n const reduce = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches\n nav.scrollBy({ left: direction * nav.clientWidth * 0.75, behavior: reduce ? 'auto' : 'smooth' })\n }\n return { overflow, page }\n}\n\n/**\n * A chevron on a solid panel at the strip's edge, as `SegmentedControl` draws\n * its own: a fade over a tab's text shows nothing. The panel's shadow paints\n * the page gutter too, so no half-hidden label shows beyond it.\n */\nfunction ScrollCue({ side, onPage }: { side: 'start' | 'end'; onPage: () => void }) {\n const start = side === 'start'\n const Icon = start ? ChevronLeft : ChevronRight\n const gutter = start ? 'calc(-1 * var(--tangle-settings-gutter, 0px))' : 'var(--tangle-settings-gutter, 0px)'\n return (\n <Button\n type=\"button\"\n variant=\"bare\"\n tabIndex={-1}\n aria-hidden=\"true\"\n data-scroll-cue={side}\n onClick={onPage}\n className={`sticky z-[1] mb-px flex shrink-0 items-center self-stretch bg-background ${start ? 'left-0 justify-start' : 'right-0 justify-end'}`}\n style={{\n width: CUE_PANEL,\n [start ? 'marginRight' : 'marginLeft']: -(CUE_PANEL + STRIP_GAP),\n boxShadow: `${gutter} 0 0 0 hsl(var(--background))`,\n }}\n >\n <span className=\"flex size-8 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm\">\n <Icon aria-hidden=\"true\" className=\"size-4\" />\n </span>\n </Button>\n )\n}\n\ninterface NavLinksProps extends SettingsNavProps {\n currentId: string\n onPick: (id: string) => void\n}\n\nfunction NavLinks({ sections, currentId, LinkComponent = PlainLink, onIntent, onPick }: NavLinksProps) {\n const listRef = useRef<HTMLUListElement>(null)\n const warmed = useRef(new Set<string>())\n const { overflow, page } = useStripOverflow(listRef)\n const intent = (section: SettingsNavSection) => {\n if (!onIntent || warmed.current.has(section.id)) return\n warmed.current.add(section.id)\n onIntent(section)\n }\n return (\n <>\n {overflow.start && <ScrollCue side=\"start\" onPage={() => page(-1)} />}\n <ul ref={listRef} role=\"list\">\n {sections.filter((section) => !section.hidden).map((section) => {\n const Icon = section.icon\n const current = section.id === currentId\n return (\n <li key={section.id}>\n <LinkComponent\n href={section.href}\n aria-current={current ? 'page' : undefined}\n data-settings-section={section.id}\n onPointerEnter={() => intent(section)}\n onFocus={() => intent(section)}\n onClick={(event) => {\n if (isPlainClick(event)) onPick(section.id)\n }}\n >\n {Icon && <Icon aria-hidden=\"true\" />}\n <span>{section.label}</span>\n {section.badge != null && <span data-count=\"\">{section.badge}</span>}\n </LinkComponent>\n </li>\n )\n })}\n </ul>\n {overflow.end && <ScrollCue side=\"end\" onPage={() => page(1)} />}\n </>\n )\n}\n\n/**\n * The section links alone, for a page that composes its own\n * `PageShell measure=\"settings\"`: pass it as the shell's `navigation`, which\n * draws the column, the tab strip and the current link.\n */\nexport function SettingsNav(props: SettingsNavProps) {\n const { currentId, setPendingId } = useOptimisticSection(props.activeId)\n return <NavLinks {...props} currentId={currentId} onPick={setPendingId} />\n}\n\n/** The decoded `#hash`, or the raw one when a hand-edited link does not decode. */\nfunction readHash(): string {\n const raw = window.location.hash.slice(1)\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n}\n\n/** Scrolls `#hash` into view and focuses its heading; without a hash, a section change starts at the top. */\nfunction useSectionScroll(activeId: string, rootRef: RefObject<HTMLDivElement | null>) {\n const first = useRef(true)\n useEffect(() => {\n const reveal = () => {\n const hash = readHash()\n const target = hash ? document.getElementById(hash) : null\n if (!target) return false\n target.scrollIntoView({ block: 'start' })\n const heading = document.getElementById(`${hash}-title`) ?? target.querySelector<HTMLElement>('h2, h3, label') ?? target\n if (!heading.hasAttribute('tabindex') && !/^(A|BUTTON|INPUT|SELECT|TEXTAREA)$/.test(heading.tagName)) heading.setAttribute('tabindex', '-1')\n heading.focus({ preventScroll: true })\n return true\n }\n if (!reveal() && !first.current) rootRef.current?.scrollIntoView({ block: 'start' })\n first.current = false\n const onHash = () => void reveal()\n window.addEventListener('hashchange', onHash)\n return () => window.removeEventListener('hashchange', onHash)\n }, [activeId, rootRef])\n}\n\n/** True once `pending` has lasted the pending delay. */\nfunction useDelayed(pending: boolean): boolean {\n const [shown, setShown] = useState(false)\n useEffect(() => {\n if (!pending) {\n setShown(false)\n return\n }\n const timer = setTimeout(() => setShown(true), PENDING_DELAY_MS)\n return () => clearTimeout(timer)\n }, [pending])\n return shown\n}\n\nexport function SettingsLayout({\n title,\n description,\n meta,\n sections,\n activeId,\n LinkComponent,\n onIntent,\n navigationLabel = 'Settings sections',\n className,\n children,\n}: SettingsLayoutProps) {\n const rootRef = useRef<HTMLDivElement>(null)\n const { currentId, pending, setPendingId } = useOptimisticSection(activeId)\n const showProgress = useDelayed(pending)\n useSectionScroll(activeId, rootRef)\n const visible = sections.filter((section) => !section.hidden)\n // PageHeader's level-2 title is the operational 20px size; titleAs keeps it the page's h1.\n // The meta line rides in the description so it keeps the 14px floor.\n const header = (\n <PageHeader\n level={2}\n titleAs=\"h1\"\n title={title}\n description={description != null || meta != null\n ? <>{description}{meta != null && <span className=\"mt-1 block\">{meta}</span>}</>\n : undefined}\n />\n )\n return (\n <div ref={rootRef} className=\"relative min-w-0\" data-settings-layout>\n {showProgress && (\n <div role=\"progressbar\" aria-label=\"Opening section\" className=\"pointer-events-none absolute inset-x-0 top-0 z-[4] h-0.5 overflow-hidden\">\n <div className=\"h-full w-1/3 animate-pulse rounded-full bg-[var(--accent-text)] motion-reduce:animate-none\" />\n </div>\n )}\n <PageShell\n measure=\"settings\"\n className={className}\n navigationLabel={navigationLabel}\n header={header}\n navigation={visible.length > 1\n ? <NavLinks sections={visible} activeId={activeId} currentId={currentId} LinkComponent={LinkComponent} onIntent={onIntent} onPick={setPendingId} />\n : undefined}\n article={children}\n />\n </div>\n )\n}\n","/**\n * The words a settings row shows after its label while a write is in flight\n * or just landed. One size (14px) and no colour at rest: \"Saved\" is the only\n * green, and it fades on its own.\n *\n * `MutationStatus` (web-react/async) is the status line for a write with no\n * settings row: it shows the moment a write starts, keeps \"Saved\" until the\n * next write, and prints the refusal itself. A row's status instead waits out\n * the pending delay, fades, and leaves the refusal to the row's error slot,\n * so its words are drawn here.\n */\n\nimport { Check, LoaderCircle } from 'lucide-react'\n\n/** How long \"Saving…\" waits before it shows (`--interaction-pending-delay`), so a fast write shows only \"Saved\". */\nexport const PENDING_DELAY_MS = 200\n\n/** How long \"Saved\" stays after a confirmed write. */\nexport const SAVED_VISIBLE_MS = 2000\n\nexport function SavingStatus({ label = 'Saving…' }: { label?: string }) {\n return (\n <span className=\"inline-flex items-center gap-1 text-sm text-muted-foreground\" data-setting-status=\"saving\">\n <LoaderCircle aria-hidden=\"true\" className=\"size-4 animate-spin motion-reduce:animate-none\" />\n {label}\n </span>\n )\n}\n\nexport function SavedStatus({ label = 'Saved' }: { label?: string }) {\n return (\n <span className=\"inline-flex items-center gap-1 text-sm text-[var(--surface-success-text)]\" data-setting-status=\"saved\">\n <Check aria-hidden=\"true\" className=\"size-4\" />\n {label}\n </span>\n )\n}\n\nexport function UnsavedStatus({ label = 'Unsaved' }: { label?: string }) {\n return <span className=\"text-sm text-muted-foreground\" data-setting-status=\"unsaved\">{label}</span>\n}\n","/**\n * One setting's save behaviour: the control shows the person's value at once,\n * the write goes through `useConfirmedMutation`, and the row says what\n * happened — \"Saving…\" only after the pending delay, \"Saved\" only on a\n * confirmed write and only for two seconds, the server's own sentence and a\n * Retry when it refused. The control keeps the person's value on a failure.\n * Writes go one at a time: a change made while one is in flight waits for it,\n * and only the newest waiting value is sent, so the server ends on the\n * person's last choice. A confirmed write the person has not edited since\n * shows the value the server kept, so a name it trims reads as saved.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport { useConfirmedMutation, type AsyncLoadContext, type MutationOutcome } from '../web-react/async'\nimport { PENDING_DELAY_MS, SAVED_VISIBLE_MS, SavedStatus, SavingStatus, UnsavedStatus } from './save-status'\n\n/**\n * - `idle`: the control shows the saved value.\n * - `unsaved`: an edit is waiting for blur or Enter (`commitOn: 'blur'`).\n * - `invalid`: the edit fails `validate`; nothing is sent.\n * - `saving`: the write is in flight.\n * - `saved`: the write was confirmed in the last two seconds.\n * - `failed`: the write was refused; the edit stays for a retry.\n */\nexport type SettingFieldState = 'idle' | 'unsaved' | 'invalid' | 'saving' | 'saved' | 'failed'\n\nexport interface UseSettingFieldOptions<T> {\n /** The value the server holds. A new value replaces the draft unless the person is editing it. */\n saved: T\n /**\n * Writes the value and resolves with the value the server kept, which the control then shows:\n * `confirmSettingResponse(fetch(...), () => next)` for a route that answers `{ success: true }`\n * or 204, a `parse` that reads the kept value from a route that answers with the record, or\n * `confirmWrite`/`rejectWrite` from `web-react/async`. A refusal's message is what the row shows.\n */\n save: (next: T, context: AsyncLoadContext) => Promise<MutationOutcome<T>>\n /** `change` (default) saves on every change: selects, switches, segmented choices. `blur` saves on `commit()`: text. */\n commitOn?: 'change' | 'blur'\n /**\n * With `commitOn: 'change'`, waits this long after the last change before saving. A change\n * still waiting when the field unmounts, as on a section switch, is sent then.\n */\n debounceMs?: number\n /** Returns the sentence that says what to fix, or null. Checked on every change; an invalid value is never sent. */\n validate?: (draft: T) => string | null\n /**\n * Defaults to structural equality over primitives, arrays and plain objects, so a `saved`\n * record built inline on each render is not a new server value. Other objects compare by\n * identity: pass one for them, such as `(a, b) => a.getTime() === b.getTime()` for a Date.\n */\n isEqual?: (a: T, b: T) => boolean\n /** Fires with the confirmed value. */\n onSaved?: (value: T) => void\n}\n\n/** Spread into `SettingsRow`: the status after the label, the error that replaces the description, and Retry. */\nexport interface SettingFieldRowProps {\n status?: ReactNode\n error?: ReactNode\n onRetry?: () => void\n}\n\nexport interface SettingField<T> {\n draft: T\n /** Shows the value at once; with `commitOn: 'change'` it also saves it. */\n setDraft: (next: T) => void\n /** Saves the draft (or `next`) unless it is invalid or already saved. Use it on blur and Enter. */\n commit: (next?: T) => void\n /** Puts the saved value back and clears any error. */\n revert: () => void\n state: SettingFieldState\n /** The validation or failure sentence, or null. */\n error: string | null\n rowProps: SettingFieldRowProps\n}\n\nfunction isPlain(value: object): boolean {\n const prototype = Object.getPrototypeOf(value)\n return Array.isArray(value) || prototype === Object.prototype || prototype === null\n}\n\n/** The default `isEqual`: the same primitive, or arrays and plain objects with the same entries. */\nfunction samePlainValue(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n if (Array.isArray(a) !== Array.isArray(b) || !isPlain(a) || !isPlain(b)) return false\n const left = a as Record<string, unknown>\n const right = b as Record<string, unknown>\n const keys = Object.keys(left)\n return keys.length === Object.keys(right).length\n && keys.every((key) => Object.prototype.hasOwnProperty.call(right, key) && samePlainValue(left[key], right[key]))\n}\n\nexport function useSettingField<T>({\n saved,\n save,\n commitOn = 'change',\n debounceMs = 0,\n validate,\n isEqual = samePlainValue,\n onSaved,\n}: UseSettingFieldOptions<T>): SettingField<T> {\n const [confirmed, setConfirmed] = useState(saved)\n const [draft, setDraftState] = useState(saved)\n const [failedValue, setFailedValue] = useState<{ value: T } | null>(null)\n const [showSaving, setShowSaving] = useState(false)\n const [showSaved, setShowSaved] = useState(false)\n const draftRef = useRef(draft)\n draftRef.current = draft\n const confirmedRef = useRef(confirmed)\n confirmedRef.current = confirmed\n const options = useRef({ validate, isEqual, onSaved })\n options.current = { validate, isEqual, onSaved }\n const debounce = useRef<ReturnType<typeof setTimeout> | null>(null)\n const savedTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const inFlight = useRef(false)\n const waiting = useRef<{ value: T } | null>(null)\n\n const mutation = useConfirmedMutation<T, T>({\n mutate: (value, context) => save(value, context),\n onSucceeded: (value) => {\n confirmedRef.current = value\n setConfirmed(value)\n setFailedValue(null)\n setShowSaved(true)\n if (savedTimer.current) clearTimeout(savedTimer.current)\n savedTimer.current = setTimeout(() => setShowSaved(false), SAVED_VISIBLE_MS)\n options.current.onSaved?.(value)\n },\n })\n const pending = mutation.state.status === 'pending'\n\n // A server value that changes under a field nobody is editing replaces it.\n // Only a new server value re-syncs, so `editing` is read, not depended on.\n const editing = !isEqual(draft, confirmed) || pending\n useEffect(() => {\n if (options.current.isEqual(saved, confirmedRef.current)) return\n setConfirmed(saved)\n if (!editing) setDraftState(saved)\n }, [saved])\n\n useEffect(() => {\n if (!pending) {\n setShowSaving(false)\n return\n }\n const timer = setTimeout(() => setShowSaving(true), PENDING_DELAY_MS)\n return () => clearTimeout(timer)\n }, [pending])\n\n const send = useCallback(async (first: T) => {\n inFlight.current = true\n let value = first\n for (;;) {\n setShowSaved(false)\n const outcome = await mutation.run(value)\n const queued = waiting.current\n waiting.current = null\n // The same value committed again while it was in flight is already written.\n const next = queued && !(outcome.succeeded && options.current.isEqual(queued.value, value)) ? queued : null\n // The server may keep a normalised form, such as a trimmed name. Unless the\n // person edited since, the control shows what was kept and nothing re-sends.\n if (outcome.succeeded && !next && options.current.isEqual(draftRef.current, value)) {\n draftRef.current = outcome.value\n setDraftState(outcome.value)\n }\n // `confirmedRef` now holds what the server keeps: this value if it landed, the earlier one if not.\n if (next && !options.current.isEqual(next.value, confirmedRef.current)) {\n value = next.value\n continue\n }\n inFlight.current = false\n if (!outcome.succeeded && !next) setFailedValue({ value })\n return\n }\n }, [mutation.run])\n\n const commit = useCallback((next?: T) => {\n if (debounce.current) {\n clearTimeout(debounce.current)\n debounce.current = null\n }\n const value = next === undefined ? draftRef.current : next\n if (options.current.validate?.(value)) return\n if (inFlight.current) {\n waiting.current = { value }\n return\n }\n if (options.current.isEqual(value, confirmedRef.current)) {\n setFailedValue(null)\n return\n }\n void send(value)\n }, [send])\n\n // A change still inside its debounce window when the field unmounts is the\n // person's last choice, so it is sent rather than dropped with the section.\n // `useConfirmedMutation` does not abort a write on unmount.\n const commitRef = useRef(commit)\n commitRef.current = commit\n useEffect(() => () => {\n if (savedTimer.current) clearTimeout(savedTimer.current)\n if (!debounce.current) return\n clearTimeout(debounce.current)\n debounce.current = null\n commitRef.current(draftRef.current)\n }, [])\n\n const setDraft = useCallback((next: T) => {\n draftRef.current = next\n setDraftState(next)\n setFailedValue(null)\n if (commitOn !== 'change') return\n if (debounce.current) clearTimeout(debounce.current)\n if (debounceMs > 0) debounce.current = setTimeout(() => commit(next), debounceMs)\n else commit(next)\n }, [commit, commitOn, debounceMs])\n\n const revert = useCallback(() => {\n if (debounce.current) clearTimeout(debounce.current)\n debounce.current = null\n waiting.current = null\n mutation.reset()\n setFailedValue(null)\n setDraftState(confirmedRef.current)\n }, [mutation.reset])\n\n const validation = validate && !isEqual(draft, confirmed) ? validate(draft) : null\n const failed = failedValue !== null && isEqual(failedValue.value, draft) && mutation.state.status === 'failed'\n const state: SettingFieldState = validation ? 'invalid'\n : pending ? 'saving'\n : failed ? 'failed'\n : !isEqual(draft, confirmed) ? 'unsaved'\n : showSaved ? 'saved'\n : 'idle'\n const error = validation ?? (failed && mutation.state.status === 'failed' ? mutation.state.message : null)\n const retry = useCallback(() => commit(draftRef.current), [commit])\n\n const rowProps = useMemo<SettingFieldRowProps>(() => ({\n status: state === 'saving' ? (showSaving ? <SavingStatus /> : undefined)\n : state === 'saved' ? <SavedStatus />\n : state === 'unsaved' && commitOn === 'blur' ? <UnsavedStatus />\n : undefined,\n error: error ?? undefined,\n onRetry: state === 'failed' ? retry : undefined,\n }), [state, showSaving, commitOn, error, retry])\n\n return { draft, setDraft, commit, revert, state, error, rowProps }\n}\n","/**\n * Typed outcomes for a settings write. A settings API answers a refusal with\n * `{ error: { code, message, field?, retryAfterSeconds? } }`; this reads that\n * body into a rejection whose message is the server's sentence, so the row\n * shows \"Only the owner can turn sources on\" instead of \"Request failed (403)\".\n * A request that never reached the server is the `network` code.\n */\n\nimport { confirmWrite, rejectWrite, type MutationOutcome, type MutationRejected } from '../web-react/async'\n\n/** The refusal codes settings routes share. A product may send others; they keep the server's message. */\nexport type SettingFailureCode = 'forbidden' | 'invalid' | 'plan_limit' | 'rate_limited' | 'not_runnable' | 'upstream' | 'network'\n\nexport interface SettingFailure {\n code: SettingFailureCode | (string & {})\n /** The sentence the row shows. */\n message: string\n /** The field an `invalid` refusal names. */\n field?: string\n /** When a `rate_limited` refusal can be tried again. */\n retryAfterSeconds?: number\n}\n\nconst FALLBACK: Record<SettingFailureCode, string> = {\n forbidden: \"You don't have permission to change this.\",\n invalid: \"That value can't be saved. Check it and try again.\",\n plan_limit: \"Your plan doesn't include this.\",\n rate_limited: \"You've reached the limit for now. Try again shortly.\",\n not_runnable: \"This can't run right now.\",\n upstream: \"A connected service didn't respond. Nothing changed; try again.\",\n network: \"Couldn't reach the server. Check your connection, then retry.\",\n}\n\nfunction codeForStatus(status: number): SettingFailureCode {\n if (status === 401 || status === 403) return 'forbidden'\n if (status === 402) return 'plan_limit'\n if (status === 429) return 'rate_limited'\n if (status === 400 || status === 409 || status === 422) return 'invalid'\n return 'upstream'\n}\n\nfunction retryWording(seconds: number): string {\n if (seconds < 90) return `Try again in ${Math.max(1, Math.round(seconds))} seconds.`\n return `Try again in ${Math.round(seconds / 60)} minutes.`\n}\n\nfunction readFailure(body: unknown, status: number): SettingFailure {\n const raw = typeof body === 'object' && body !== null ? (body as { error?: unknown }).error : undefined\n const error = typeof raw === 'string' ? { message: raw } : typeof raw === 'object' && raw !== null ? raw as Record<string, unknown> : {}\n const code = typeof error.code === 'string' && error.code ? error.code : codeForStatus(status)\n const retryAfterSeconds = typeof error.retryAfterSeconds === 'number' ? error.retryAfterSeconds : undefined\n const given = typeof error.message === 'string' && error.message.trim() ? error.message.trim() : null\n let message = given ?? FALLBACK[code as SettingFailureCode] ?? FALLBACK.upstream\n if (retryAfterSeconds !== undefined && code === 'rate_limited' && !given) message = `You've reached the limit for now. ${retryWording(retryAfterSeconds)}`\n return {\n code,\n message,\n ...(typeof error.field === 'string' ? { field: error.field } : {}),\n ...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),\n }\n}\n\n/**\n * Confirms a settings write from its response, or rejects it with the\n * server's typed failure. Pass the `fetch` promise itself so a request that\n * never reached the server becomes a `network` failure rather than a throw.\n * `parse` returns the value the server kept: read it from the 2xx body when\n * the route answers with the saved record, or return the value sent\n * (`() => next`) when it answers `{ success: true }` or 204. Without `parse`\n * the value is the body, typed `unknown`, so it cannot stand in for a field's\n * value.\n */\nexport function confirmSettingResponse(request: Response | Promise<Response>): Promise<MutationOutcome<unknown>>\nexport function confirmSettingResponse<T>(request: Response | Promise<Response>, parse: (data: unknown) => T): Promise<MutationOutcome<T>>\nexport async function confirmSettingResponse<T>(\n request: Response | Promise<Response>,\n parse?: (data: unknown) => T,\n): Promise<MutationOutcome<unknown>> {\n let response: Response\n try {\n response = await request\n } catch (error) {\n return rejectWrite(FALLBACK.network, { code: 'network', message: FALLBACK.network, cause: error } satisfies SettingFailure & { cause: unknown })\n }\n const text = await response.text().catch(() => '')\n let body: unknown\n try {\n body = text ? JSON.parse(text) : undefined\n } catch {\n body = undefined\n }\n if (!response.ok) {\n const failure = readFailure(body, response.status)\n return rejectWrite(failure.message, failure)\n }\n try {\n return confirmWrite(parse ? parse(body) : body)\n } catch (error) {\n return rejectWrite('The server answered, but not with the saved value. Reload to see what was kept.', error)\n }\n}\n\n/** The typed failure behind a rejected settings write (or a mutation's `failed` state), when it carries one. */\nexport function settingFailure(outcome: Pick<MutationRejected, 'message' | 'error'>): SettingFailure {\n const error = outcome.error as Partial<SettingFailure> | undefined\n return typeof error === 'object' && error !== null && typeof error.code === 'string'\n ? { ...error, code: error.code, message: outcome.message }\n : { code: 'upstream', message: outcome.message }\n}\n","/**\n * The confirmation for a setting that costs money, reaches people, removes\n * something or acts outside the product. It states the consequence, any\n * from → to changes and an optional notice, can require the person to type a\n * name, and closes only when the write is confirmed: a refusal stays in the\n * dialog with the server's sentence, and any action that answers it, such as\n * View plans for a plan limit, so the person can act, try again or cancel.\n *\n * Wide screens get an `AlertDialog`; below 40rem it is a bottom `Sheet` with\n * full-width buttons, the confirming one first.\n */\n\nimport {\n Alert,\n AlertDialog,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Button,\n Input,\n Label,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from '@tangle-network/ui/primitives'\nimport { CircleAlert } from 'lucide-react'\nimport { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore, type ReactElement, type ReactNode } from 'react'\nimport { useConfirmedMutation, type MutationOutcome } from '../web-react/async'\nimport { settingFailure, type SettingFailure } from './setting-outcome'\n\n/** One value the action changes, shown as `label: from → to`. */\nexport interface ConfirmChange {\n label: string\n from: ReactNode\n to: ReactNode\n}\n\nexport interface ConfirmActionProps<T = unknown> {\n /** The control that opens the dialog, such as a `Button`. Leave it out and drive `open` from a menu item. */\n trigger?: ReactElement\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /** A question naming the action: \"Delete Fieldnote GTM?\" */\n title: string\n /** What happens, in one or two sentences: cost, reach, what is removed and whether it can be undone. */\n consequence: ReactNode\n changes?: ConfirmChange[]\n /** A note the person should read before confirming, such as what else this allows. */\n notice?: ReactNode\n /** The verb on the confirming button: \"Delete project\", \"Turn on\". */\n confirmLabel: string\n cancelLabel?: string\n /** `danger` draws the confirming button destructive. */\n tone?: 'default' | 'danger'\n /** Fields the action needs, such as an address and a role. Drawn between the notice and `requireText`. */\n children?: ReactNode\n /** False keeps the confirming button unavailable, such as until a live check passes. */\n canConfirm?: boolean\n /** The text the person types before the action is available, such as the project name. */\n requireText?: string\n /** Performs the action. Only a confirmed outcome closes the dialog. */\n onConfirm: () => Promise<MutationOutcome<T>>\n /** Runs after a confirmed outcome, with its value: navigate away, refresh a list. */\n onConfirmed?: (value: T) => void\n /** The action shown after a refusal's sentence, such as a View plans link for `plan_limit`; null shows none. */\n refusalAction?: (failure: SettingFailure) => ReactNode\n}\n\nconst NARROW = '(max-width: 40rem)'\n\nfunction subscribeNarrow(onChange: () => void): () => void {\n if (typeof window === 'undefined' || !window.matchMedia) return () => undefined\n const query = window.matchMedia(NARROW)\n query.addEventListener('change', onChange)\n return () => query.removeEventListener('change', onChange)\n}\n\n/** A menu's exit animation is 150ms; a menu still on the page after this is not closing. */\nconst MENU_EXIT_LIMIT_MS = 1000\n\n/**\n * `open`, held back while the ⋯ menu that opened the dialog is still closing.\n * ui 11.40.0's `AlertDialog` and `DropdownMenu` run different copies of\n * Radix's dismissable layer, which do not see each other: a dialog that mounts\n * while the menu that opened it is still closing records the menu's pointer\n * lock as the page's own and puts it back when it closes, leaving the page\n * unclickable. Only the menus on the page when `open` turned true are waited\n * for, and for at most `MENU_EXIT_LIMIT_MS`, so a menu that stays open, or one\n * opened later, never keeps the dialog shut.\n */\nfunction useOpenAfterMenus(open: boolean): boolean {\n const [clear, setClear] = useState(false)\n useEffect(() => {\n if (!open) {\n setClear(false)\n return\n }\n const closing = Array.from(document.querySelectorAll('[role=\"menu\"]'))\n const deadline = performance.now() + MENU_EXIT_LIMIT_MS\n let frame = 0\n const wait = () => {\n if (closing.some((menu) => menu.isConnected) && performance.now() < deadline) frame = requestAnimationFrame(wait)\n else setClear(true)\n }\n wait()\n return () => cancelAnimationFrame(frame)\n }, [open])\n return open && clear\n}\n\nfunction useNarrowViewport(): boolean {\n return useSyncExternalStore(\n subscribeNarrow,\n () => typeof window !== 'undefined' && Boolean(window.matchMedia?.(NARROW).matches),\n () => false,\n )\n}\n\nexport function ConfirmAction<T = unknown>({\n trigger,\n open: openProp,\n onOpenChange,\n title,\n consequence,\n changes,\n notice,\n confirmLabel,\n cancelLabel = 'Cancel',\n tone = 'default',\n children,\n canConfirm = true,\n requireText,\n onConfirm,\n onConfirmed,\n refusalAction,\n}: ConfirmActionProps<T>) {\n const [innerOpen, setInnerOpen] = useState(false)\n const open = openProp ?? innerOpen\n const shown = useOpenAfterMenus(open)\n const [typed, setTyped] = useState('')\n const narrow = useNarrowViewport()\n const bodyRef = useRef<HTMLDivElement>(null)\n const cancelRef = useRef<HTMLButtonElement>(null)\n const inputId = useId()\n const errorId = useId()\n\n const mutation = useConfirmedMutation<void, T>({ mutate: () => onConfirm(), concurrency: 'reject' })\n const pending = mutation.state.status === 'pending'\n const failure = mutation.state.status === 'failed' ? settingFailure(mutation.state) : null\n const failureAction = failure && refusalAction?.(failure)\n const { reset } = mutation\n\n const setOpen = useCallback((next: boolean) => {\n // A write in flight keeps the dialog: closing it would hide its outcome.\n if (!next && pending) return\n setInnerOpen(next)\n onOpenChange?.(next)\n }, [onOpenChange, pending])\n\n useEffect(() => {\n if (!open) return\n setTyped('')\n reset()\n }, [open, reset])\n\n const ready = canConfirm && (requireText === undefined || typed.trim() === requireText)\n const confirm = async () => {\n if (!ready || pending) return\n const outcome = await mutation.run()\n if (!outcome.succeeded) return\n setInnerOpen(false)\n onOpenChange?.(false)\n onConfirmed?.(outcome.value)\n }\n\n // A dialog with fields opens on its first field; otherwise on Cancel, so Enter never confirms by accident.\n // Focus moves a task later: a ⋯ menu that opened the dialog still holds focus in its own trap until it closes.\n const focusFirst = (event: Event) => {\n event.preventDefault()\n setTimeout(() => {\n const target = bodyRef.current?.querySelector<HTMLElement>('input, textarea, [role=\"combobox\"]') ?? cancelRef.current\n target?.focus()\n }, 0)\n }\n\n const hasBody = Boolean(changes?.length) || notice != null || children != null || requireText !== undefined || failure != null\n const body = hasBody && (\n <div ref={bodyRef} className=\"grid min-w-0 gap-4 text-sm\">\n {changes && changes.length > 0 && (\n <dl className=\"grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-x-4 gap-y-2 rounded-lg border border-border px-4 py-3\">\n {changes.map((change) => (\n <div key={change.label} className=\"contents\">\n <dt className=\"text-muted-foreground\">{change.label}</dt>\n <dd className=\"min-w-0 [overflow-wrap:anywhere]\">\n <span className=\"text-muted-foreground\">{change.from}</span>\n <span aria-hidden=\"true\" className=\"px-1.5 text-muted-foreground\">→</span>\n <span className=\"sr-only\"> to </span>\n <span className=\"font-medium text-foreground\">{change.to}</span>\n </dd>\n </div>\n ))}\n </dl>\n )}\n {notice != null && <Alert tone=\"info\">{notice}</Alert>}\n {children}\n {requireText !== undefined && (\n <div className=\"grid gap-2\">\n <Label htmlFor={inputId}>Type <span className=\"font-semibold [overflow-wrap:anywhere]\">{requireText}</span> to confirm</Label>\n <Input\n id={inputId}\n value={typed}\n autoComplete=\"off\"\n spellCheck={false}\n aria-describedby={failure ? errorId : undefined}\n onChange={(event) => setTyped(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void confirm()\n }\n }}\n />\n </div>\n )}\n {failure && (\n <p id={errorId} role=\"alert\" className=\"flex items-start gap-1.5 text-[var(--surface-danger-text)] [overflow-wrap:anywhere]\">\n <CircleAlert aria-hidden=\"true\" className=\"mt-0.5 size-4 shrink-0\" />\n <span className=\"min-w-0\">{failure.message}{failureAction != null && <>{' '}{failureAction}</>}</span>\n </p>\n )}\n </div>\n )\n\n const confirmButton = (\n <Button\n type=\"button\"\n variant={tone === 'danger' ? 'destructive' : 'default'}\n loading={pending}\n disabled={!ready || pending}\n onClick={() => void confirm()}\n data-confirm-action\n >\n {confirmLabel}\n </Button>\n )\n\n if (narrow) {\n return (\n <Sheet open={shown} onOpenChange={setOpen}>\n {trigger && <SheetTrigger asChild>{trigger}</SheetTrigger>}\n <SheetContent side=\"bottom\" hideCloseButton onOpenAutoFocus={focusFirst} className=\"max-h-[90dvh] overflow-y-auto\">\n <SheetHeader className=\"pb-0\">\n <SheetTitle>{title}</SheetTitle>\n <SheetDescription>{consequence}</SheetDescription>\n </SheetHeader>\n {body && <div className=\"px-4\">{body}</div>}\n <SheetFooter className=\"pt-2 pb-[max(1rem,env(safe-area-inset-bottom))]\">\n {confirmButton}\n <Button ref={cancelRef} type=\"button\" variant=\"outline\" disabled={pending} onClick={() => setOpen(false)}>{cancelLabel}</Button>\n </SheetFooter>\n </SheetContent>\n </Sheet>\n )\n }\n\n return (\n <AlertDialog open={shown} onOpenChange={setOpen}>\n {trigger && <AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger>}\n <AlertDialogContent onOpenAutoFocus={focusFirst}>\n <AlertDialogHeader>\n <AlertDialogTitle>{title}</AlertDialogTitle>\n <AlertDialogDescription>{consequence}</AlertDialogDescription>\n </AlertDialogHeader>\n {body}\n <AlertDialogFooter>\n <AlertDialogCancel ref={cancelRef} disabled={pending}>{cancelLabel}</AlertDialogCancel>\n {confirmButton}\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n )\n}\n","/**\n * The one save bar a group of settings may have, for values that only make\n * sense saved together (a set of spend caps). Pass it as a `SettingsSection`\n * footer. Save is the primary action only while there are changes; a refusal\n * shows its sentence here and the edits stay. \"Saved\" shows for two seconds\n * after `state` becomes `saved`, as a row's does. On a narrow card with\n * unsaved changes the footer sticks to the bottom of the screen, so Save stays\n * in reach while the person scrolls the fields above it.\n */\n\nimport { Button } from '@tangle-network/ui/primitives'\nimport { CircleAlert, Lock } from 'lucide-react'\nimport { useEffect, useState, type ReactNode } from 'react'\nimport { SAVED_VISIBLE_MS, SavedStatus } from './save-status'\n\n// These style the footer the bar sits in (`:has(> bar)`): the bar is that\n// footer's only child, so only the footer can stick within the card.\nconst STICKY_FOOTER = '@max-[34rem]/settings-section:[:has(>&)]:sticky @max-[34rem]/settings-section:[:has(>&)]:bottom-0 @max-[34rem]/settings-section:[:has(>&)]:z-10'\n\nexport interface SettingsSaveBarProps {\n /** How many values differ from the saved ones. */\n changes: number\n /** Replaces the \"2 unsaved changes\" count. */\n summary?: ReactNode\n onDiscard: () => void\n onSave: () => void\n state: 'idle' | 'saving' | 'saved' | 'failed'\n /** The refusal to show while `state` is `failed`. */\n error?: ReactNode\n /** The verb on the save button. */\n saveLabel?: string\n /** Why the person cannot save, naming who can. Replaces the buttons' availability. */\n disabledReason?: ReactNode\n}\n\nexport function SettingsSaveBar({\n changes,\n summary,\n onDiscard,\n onSave,\n state,\n error,\n saveLabel = 'Save changes',\n disabledReason,\n}: SettingsSaveBarProps) {\n const saving = state === 'saving'\n const dirty = changes > 0\n const [showSaved, setShowSaved] = useState(state === 'saved')\n useEffect(() => {\n setShowSaved(state === 'saved')\n if (state !== 'saved') return\n const timer = setTimeout(() => setShowSaved(false), SAVED_VISIBLE_MS)\n return () => clearTimeout(timer)\n }, [state])\n const message = disabledReason != null\n ? <span className=\"flex items-start gap-1.5 text-muted-foreground\"><Lock aria-hidden=\"true\" className=\"mt-0.5 size-4 shrink-0\" /><span className=\"min-w-0\">{disabledReason}</span></span>\n : state === 'failed' && error != null\n ? <span role=\"alert\" className=\"flex items-start gap-1.5 text-[var(--surface-danger-text)]\"><CircleAlert aria-hidden=\"true\" className=\"mt-0.5 size-4 shrink-0\" /><span className=\"min-w-0 [overflow-wrap:anywhere]\">{error}</span></span>\n : summary ?? (dirty\n ? <span className=\"text-muted-foreground\">{changes === 1 ? '1 unsaved change' : `${changes} unsaved changes`}</span>\n : showSaved ? <SavedStatus /> : <span className=\"text-muted-foreground\">No unsaved changes</span>)\n const sticky = dirty && disabledReason == null\n return (\n <div\n className={`flex min-w-0 flex-1 flex-wrap items-center justify-between gap-x-4 gap-y-2${sticky ? ` ${STICKY_FOOTER}` : ''}`}\n data-settings-save-bar\n data-dirty={sticky || undefined}\n >\n <div aria-live=\"polite\" className=\"min-w-0 text-sm\">{message}</div>\n {disabledReason == null && (\n <div className=\"ml-auto flex shrink-0 gap-2\">\n {dirty && <Button type=\"button\" variant=\"ghost\" disabled={saving} onClick={onDiscard}>Discard</Button>}\n <Button type=\"button\" variant={dirty ? 'default' : 'outline'} disabled={!dirty || saving} loading={saving} onClick={onSave}>\n {saveLabel}\n </Button>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Who has access and what each person can do, as settings cards built on the\n * teams panels' contracts and rules: the same `MemberView` and\n * `InvitationView` rows, the same role ladder and invite check, and roles\n * named by capability. A role change saves at once and says so in its row;\n * removing a person or revoking an invitation asks first and stays open on a\n * refusal. On a narrow card the role picker takes its own full-width line\n * under the name.\n */\n\nimport {\n Avatar,\n AvatarFallback,\n Button,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n Input,\n Label,\n SettingsList,\n SettingsListItem,\n SettingsRow,\n SettingsSection,\n type SettingsListMenuItem,\n} from '@tangle-network/ui/primitives'\nimport { ChevronDown } from 'lucide-react'\nimport { Fragment, useEffect, useId, useState, type ReactNode } from 'react'\nimport { INVITATION_EMAIL_HINT, INVITATION_EXPIRY_DAYS, isInvitationEmail } from '../teams/invitations'\nimport { hasWorkspaceRole, type WorkspaceRole } from '../teams/roles'\nimport type { InvitationView, MemberView } from '../teams-react/contracts'\nimport { invitationDetail } from '../teams-react/components/invitation-detail'\nimport { canManageMember, isCurrentMember, memberRoleLabel } from '../teams-react/components/member-access'\nimport { RoleSelect } from '../teams-react/components/RoleSelect'\nimport { confirmWrite, type MutationOutcome } from '../web-react/async'\nimport { ConfirmAction } from './confirm-action'\nimport { PlainLink, type SettingsLinkComponent } from './settings-layout'\nimport type { SettingFailure } from './setting-outcome'\nimport { SAVED_VISIBLE_MS } from './save-status'\nimport { useSettingField } from './use-setting-field'\n\nexport interface MembersSectionProps {\n /** The card's anchor. */\n id?: string\n title?: string\n /** One line under the title, such as the seat count. */\n description?: ReactNode\n members: MemberView[]\n /** The viewer's effective role: admins change roles, remove people and invite. */\n currentRole: WorkspaceRole\n /** The viewer's user id, so their own row offers no role change or removal. */\n currentUserId?: string | null\n /** The noun people lose access to, as in \"lose access to this project\". */\n noun?: string\n onChangeRole(input: { memberId: string; role: WorkspaceRole }): Promise<MutationOutcome<unknown>>\n onRemove(input: { memberId: string }): Promise<MutationOutcome<unknown>>\n /** Adds the Invite action. A refusal (a seat limit, a failed email) stays in the dialog with its sentence. */\n onInvite?(input: { email: string; role: WorkspaceRole }): Promise<MutationOutcome<unknown>>\n /** The plans page. A `plan_limit` refusal on Invite links to it as View plans. */\n plansHref?: string\n /** Renders View plans: pass the router's link, as to `SettingsLayout`. Defaults to a plain anchor. */\n LinkComponent?: SettingsLinkComponent\n /** More ⋯ items for a member the viewer can manage, such as \"Make team owner…\". */\n menuItems?(member: MemberView): SettingsListMenuItem[]\n}\n\n/** An address that wraps after `@`, a dot or a hyphen before it wraps mid-word. */\nfunction breakable(address: string): ReactNode {\n const parts = address.split(/(?<=[@.\\-_])/)\n return parts.map((part, index) => <Fragment key={index}>{part}{index < parts.length - 1 && <wbr />}</Fragment>)\n}\n\nfunction displayName(member: MemberView): string {\n return member.name ?? member.email ?? 'Unknown'\n}\n\nfunction initials(member: MemberView): string {\n const name = displayName(member)\n const parts = name.split(/[\\s@._-]+/).filter(Boolean)\n return ((parts[0]?.[0] ?? '?') + (member.name ? parts[1]?.[0] ?? '' : '')).toUpperCase()\n}\n\n// The avatar rides in the title rather than a leading column, so on a narrow\n// card the role picker's own line starts at the card's edge and the longest\n// capability name fits.\nconst MEMBER_COLUMNS = { status: '18rem', menu: '36px' }\n\n/** A member's role, saved the moment it is picked. */\nfunction useMemberRole(member: MemberView, onChangeRole: MembersSectionProps['onChangeRole']) {\n const field = useSettingField<WorkspaceRole>({\n saved: member.role,\n save: async (role) => {\n const outcome = await onChangeRole({ memberId: member.id, role })\n return outcome.succeeded ? confirmWrite(role) : outcome\n },\n })\n return { field, select: <RoleSelect id={`role-${member.id}`} value={field.draft} ariaLabel={`Role for ${displayName(member)}`} onChange={field.setDraft} /> }\n}\n\nfunction MemberItem({ member, editable, self, noun, onChangeRole, onRemove, extraMenu }: {\n member: MemberView\n editable: boolean\n self: boolean\n noun: string\n onChangeRole: MembersSectionProps['onChangeRole']\n onRemove: MembersSectionProps['onRemove']\n extraMenu: SettingsListMenuItem[]\n}) {\n const [removing, setRemoving] = useState(false)\n const { field, select } = useMemberRole(member, onChangeRole)\n const name = displayName(member)\n const pending = member.acceptedAt == null\n const email = member.name && member.email ? member.email : null\n const status = field.rowProps.status\n return (\n <>\n <SettingsListItem\n title={\n <span className=\"flex min-w-0 items-center gap-3\">\n <Avatar size=\"sm\"><AvatarFallback>{initials(member)}</AvatarFallback></Avatar>\n <span className=\"min-w-0\">\n <span className=\"block\">{name}{self && <span className=\"font-normal text-muted-foreground\"> (you)</span>}</span>\n {(email || pending || status) && (\n <span className=\"flex flex-wrap items-center gap-x-2 font-normal text-muted-foreground\">\n <span>{email && breakable(email)}{email && pending && ' · '}{pending && 'Hasn’t joined yet'}</span>\n {status}\n </span>\n )}\n </span>\n </span>\n }\n status={editable ? select : <span className=\"text-foreground\">{memberRoleLabel(member)}</span>}\n error={field.rowProps.error}\n menu={editable ? [...extraMenu, { label: `Remove from ${noun}…`, tone: 'danger', onSelect: () => setRemoving(true) }] : undefined}\n />\n {editable && (\n <ConfirmAction\n open={removing}\n onOpenChange={setRemoving}\n title={`Remove ${name}?`}\n consequence={`${name} loses access to this ${noun} at once. Their chats and files stay.`}\n confirmLabel=\"Remove\"\n tone=\"danger\"\n onConfirm={() => onRemove({ memberId: member.id })}\n />\n )}\n </>\n )\n}\n\n/** View plans after a plan-limit refusal, as an inline link after the sentence. */\nfunction plansAction(href: string | undefined, Link: SettingsLinkComponent) {\n if (!href) return undefined\n return (failure: SettingFailure) => failure.code === 'plan_limit'\n ? <Link href={href} className=\"font-medium text-[var(--accent-text)] underline-offset-4 hover:underline\">View plans</Link>\n : null\n}\n\nfunction InviteDialog({ onInvite, plansHref, LinkComponent, open, onOpenChange }: {\n onInvite: NonNullable<MembersSectionProps['onInvite']>\n plansHref?: string\n LinkComponent: SettingsLinkComponent\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const id = useId()\n const [email, setEmail] = useState('')\n const [role, setRole] = useState<WorkspaceRole>('editor')\n const [checked, setChecked] = useState(false)\n useEffect(() => {\n if (!open) return\n setEmail('')\n setRole('editor')\n setChecked(false)\n }, [open])\n const valid = isInvitationEmail(email)\n const showHint = checked && email.trim() !== '' && !valid\n return (\n <ConfirmAction\n open={open}\n onOpenChange={onOpenChange}\n title=\"Invite people\"\n consequence={`They get an email with a link to join. The link works for ${INVITATION_EXPIRY_DAYS} days.`}\n confirmLabel=\"Send invite\"\n canConfirm={valid}\n onConfirm={() => onInvite({ email: email.trim(), role })}\n refusalAction={plansAction(plansHref, LinkComponent)}\n >\n <div className=\"grid gap-2\">\n <Label htmlFor={`${id}-email`}>Email</Label>\n <Input\n id={`${id}-email`}\n type=\"email\"\n inputMode=\"email\"\n autoComplete=\"off\"\n value={email}\n variant={showHint ? 'error' : undefined}\n aria-describedby={showHint ? `${id}-hint` : undefined}\n onChange={(event) => setEmail(event.target.value)}\n onBlur={() => setChecked(true)}\n />\n {showHint && <p id={`${id}-hint`} role=\"alert\" className=\"text-sm text-[var(--surface-danger-text)]\">{INVITATION_EMAIL_HINT}</p>}\n </div>\n <div className=\"grid gap-2\">\n <Label htmlFor={`${id}-role`}>Role</Label>\n <RoleSelect id={`${id}-role`} value={role} ariaLabel=\"Role\" onChange={setRole} />\n </div>\n </ConfirmAction>\n )\n}\n\nexport function MembersSection({\n id = 'members',\n title = 'Members',\n description,\n members,\n currentRole,\n currentUserId,\n noun = 'project',\n onChangeRole,\n onRemove,\n onInvite,\n plansHref,\n LinkComponent = PlainLink,\n menuItems,\n}: MembersSectionProps) {\n const [inviting, setInviting] = useState(false)\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n return (\n <SettingsSection\n id={id}\n title={title}\n description={description}\n actions={canManage && onInvite ? <Button type=\"button\" onClick={() => setInviting(true)}>Invite</Button> : undefined}\n >\n {members.length === 0\n ? <SettingsRow label=\"No members yet\" description=\"Invite someone to work on this with you.\" layout=\"compact\" />\n : (\n <SettingsList aria-label={title} columns={MEMBER_COLUMNS}>\n {members.map((member) => {\n const editable = canManageMember(member, currentRole, currentUserId)\n return (\n <MemberItem\n key={member.id}\n member={member}\n editable={editable}\n self={isCurrentMember(member, currentUserId)}\n noun={noun}\n onChangeRole={onChangeRole}\n onRemove={onRemove}\n extraMenu={editable && menuItems ? menuItems(member) : []}\n />\n )\n })}\n </SettingsList>\n )}\n {onInvite && <InviteDialog onInvite={onInvite} plansHref={plansHref} LinkComponent={LinkComponent} open={inviting} onOpenChange={setInviting} />}\n </SettingsSection>\n )\n}\n\nexport interface InvitationsSectionProps {\n id?: string\n title?: string\n invitations: InvitationView[]\n /** The viewer's effective role: admins copy, resend and revoke. */\n currentRole: WorkspaceRole\n onResend(input: { invitationId: string }): Promise<MutationOutcome<unknown>>\n onRevoke(input: { invitationId: string }): Promise<MutationOutcome<unknown>>\n /** Copies the invite link; defaults to `navigator.clipboard.writeText`. */\n onCopy?(input: { inviteUrl: string }): void | Promise<void>\n}\n\nconst INVITATION_COLUMNS = { action: '8rem', menu: '36px' }\n\nfunction useFlash(): [string | null, (message: string) => void] {\n const [message, setMessage] = useState<string | null>(null)\n useEffect(() => {\n if (!message) return\n const timer = setTimeout(() => setMessage(null), SAVED_VISIBLE_MS)\n return () => clearTimeout(timer)\n }, [message])\n return [message, setMessage]\n}\n\nfunction PendingInvitation({ invitation, canManage, onResend, onRevoke, onCopy }: {\n invitation: InvitationView\n canManage: boolean\n} & Pick<InvitationsSectionProps, 'onResend' | 'onRevoke' | 'onCopy'>) {\n const [revoking, setRevoking] = useState(false)\n const [note, flash] = useFlash()\n const [error, setError] = useState<string | null>(null)\n const copy = async () => {\n try {\n if (onCopy) await onCopy({ inviteUrl: invitation.inviteUrl })\n else await navigator.clipboard.writeText(invitation.inviteUrl)\n setError(null)\n flash('Link copied')\n } catch {\n setError(\"Couldn't copy the link. Select it from the invitation email instead.\")\n }\n }\n const resend = async () => {\n const outcome = await onResend({ invitationId: invitation.id })\n if (outcome.succeeded) {\n setError(null)\n flash('Sent again')\n } else setError(outcome.message)\n }\n const undelivered = invitation.emailStatus === 'failed' ? 'The email wasn’t delivered. Copy the link and send it yourself.' : null\n return (\n <>\n <SettingsListItem\n title={breakable(invitation.email)}\n meta={<span className=\"inline-flex flex-wrap items-center gap-x-2\">{invitationDetail(invitation)}{note && <span className=\"text-[var(--surface-success-text)]\">{note}</span>}</span>}\n error={error ?? undelivered ?? undefined}\n action={canManage ? { label: 'Copy link', onSelect: () => void copy() } : undefined}\n menu={canManage ? [\n { label: 'Resend email', onSelect: () => void resend() },\n { label: 'Revoke…', tone: 'danger', onSelect: () => setRevoking(true) },\n ] : undefined}\n />\n {canManage && (\n <ConfirmAction\n open={revoking}\n onOpenChange={setRevoking}\n title={`Revoke the invitation to ${invitation.email}?`}\n consequence=\"The link stops working at once. You can invite them again later.\"\n confirmLabel=\"Revoke\"\n tone=\"danger\"\n onConfirm={() => onRevoke({ invitationId: invitation.id })}\n />\n )}\n </>\n )\n}\n\nexport function InvitationsSection({\n id = 'invitations',\n title = 'Invitations',\n invitations,\n currentRole,\n onResend,\n onRevoke,\n onCopy,\n}: InvitationsSectionProps) {\n const [showPast, setShowPast] = useState(false)\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n const pending = invitations.filter((invitation) => invitation.status === 'pending')\n const past = invitations.filter((invitation) => invitation.status !== 'pending')\n return (\n <SettingsSection id={id} title={title}>\n {pending.length === 0\n ? <SettingsRow label=\"No pending invitations\" layout=\"compact\" />\n : (\n <SettingsList aria-label=\"Pending invitations\" columns={INVITATION_COLUMNS}>\n {pending.map((invitation) => (\n <PendingInvitation key={invitation.id} invitation={invitation} canManage={canManage} onResend={onResend} onRevoke={onRevoke} onCopy={onCopy} />\n ))}\n </SettingsList>\n )}\n {past.length > 0 && (\n <Collapsible open={showPast} onOpenChange={setShowPast}>\n <div className=\"border-t border-[color:var(--border-subtle)] px-5 py-3 @max-[34rem]/settings-section:px-4\">\n <CollapsibleTrigger asChild>\n <Button type=\"button\" variant=\"ghost\" className=\"-ml-4\">\n {showPast ? 'Hide past invitations' : `Show ${past.length} past ${past.length === 1 ? 'invitation' : 'invitations'}`}\n <ChevronDown aria-hidden=\"true\" className={showPast ? 'rotate-180' : undefined} />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <SettingsList aria-label=\"Past invitations\" columns={{}}>\n {past.map((invitation) => (\n <SettingsListItem key={invitation.id} title={breakable(invitation.email)} meta={invitationDetail(invitation)} />\n ))}\n </SettingsList>\n </CollapsibleContent>\n </Collapsible>\n )}\n </SettingsSection>\n )\n}\n","/**\n * Export as a settings card: the workspace export (the same engine and\n * resume behaviour as `WorkspaceExportPanel`) drawn as rows, with any other\n * copy or download rows the product adds below it. A viewer who cannot export\n * sees the row locked with the reason instead of a missing feature.\n */\n\nimport {\n Button,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n Progress,\n SettingsRow,\n SettingsSection,\n} from '@tangle-network/ui/primitives'\nimport { ChevronDown } from 'lucide-react'\nimport { useState, type ReactNode } from 'react'\nimport { useWorkspaceExport } from '../workspace-export/react'\nimport type { ExportProgress } from '../workspace-export/types'\n\nexport interface ExportSectionProps {\n /** The card's anchor. */\n id?: string\n title?: string\n /** The mount path of the product's export routes, e.g. `/api/workspaces/ws-1/exports`. */\n endpoint: string\n /** Whether the viewer may export; the server refuses others regardless. */\n canExport: boolean\n /** Why the viewer cannot export, naming who can. Without it a viewer who cannot export sees no export row. */\n disabledReason?: ReactNode\n /** The export row's one line. */\n description?: ReactNode\n fetcher?: typeof fetch\n /** True when the product finishes exports on a schedule after the page closes. */\n continuesInBackground?: boolean\n /** More rows for this card, such as a JSON download or \"Duplicate project…\". */\n children?: ReactNode\n}\n\nconst BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB']\n\nfunction formatBytes(bytes: number): string {\n let value = bytes\n let unit = 0\n while (value >= 1024 && unit < BYTE_UNITS.length - 1) {\n value /= 1024\n unit += 1\n }\n return `${value < 10 && unit ? value.toFixed(1) : Math.round(value)} ${BYTE_UNITS[unit]}`\n}\n\nfunction unitState(unit: ExportProgress['units'][number]): string {\n if (unit.status === 'pending') return 'Waiting'\n if (unit.status === 'empty') return 'Nothing to export'\n if (unit.status === 'failed') return 'Failed'\n if (unit.rows !== undefined) return `${unit.rows} rows`\n if (unit.files !== undefined) return `${unit.files} files · ${formatBytes(unit.bytes)}`\n return formatBytes(unit.bytes)\n}\n\nfunction progressLine(latest: ExportProgress): string {\n return [\n `${latest.unitsDone} of ${latest.unitsTotal} sources`,\n latest.unitsFailed ? `${latest.unitsFailed} failed` : null,\n formatBytes(latest.bytes),\n latest.status === 'running' && latest.current ? `now ${latest.current}` : null,\n ].filter(Boolean).join(' · ')\n}\n\nfunction LatestExport({ latest, continuesInBackground }: { latest: ExportProgress; continuesInBackground: boolean }) {\n const [open, setOpen] = useState(false)\n const percent = latest.unitsTotal ? Math.round((latest.unitsDone / latest.unitsTotal) * 100) : 0\n const label = latest.status === 'running' ? 'Preparing your export' : latest.status === 'complete' ? 'Your export is ready' : 'The export failed'\n return (\n <SettingsRow\n label={label}\n layout=\"stacked\"\n description={latest.status === 'failed' ? undefined : progressLine(latest)}\n error={latest.status === 'failed' ? `${latest.error ?? 'A source could not be read.'} Start a new export to try again.` : undefined}\n >\n <div className=\"grid w-full min-w-0 gap-3 text-sm\">\n {latest.status === 'running' && (\n <>\n <Progress value={percent} aria-label=\"Export progress\" />\n {continuesInBackground && <p className=\"text-muted-foreground\">You can close this page. The export keeps going, and the download is here when you return.</p>}\n </>\n )}\n {latest.downloadUrl && (\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-2\">\n <Button asChild variant=\"outline\"><a href={latest.downloadUrl} download>Download zip</a></Button>\n <span className=\"text-muted-foreground\">\n Link expires {latest.downloadExpiresAt ? new Date(latest.downloadExpiresAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : 'soon'}; reload for a new one.\n </span>\n </div>\n )}\n <Collapsible open={open} onOpenChange={setOpen}>\n <CollapsibleTrigger asChild>\n <Button type=\"button\" variant=\"ghost\" className=\"-ml-4\">\n {open ? 'Hide sources' : `Show ${latest.units.length} sources`}\n <ChevronDown aria-hidden=\"true\" className={open ? 'rotate-180' : undefined} />\n </Button>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <ul className=\"mt-1 divide-y divide-[color:var(--border-subtle)] rounded-lg border border-border\">\n {latest.units.map((unit) => (\n <li key={unit.name} className=\"flex flex-wrap items-center justify-between gap-x-3 gap-y-1 px-3 py-2\">\n <span className=\"min-w-0 [overflow-wrap:anywhere]\">{unit.name}</span>\n <span className={unit.status === 'failed' ? 'text-[var(--surface-danger-text)]' : 'text-muted-foreground'}>{unitState(unit)}</span>\n </li>\n ))}\n </ul>\n </CollapsibleContent>\n </Collapsible>\n </div>\n </SettingsRow>\n )\n}\n\nexport function ExportSection({\n id = 'export',\n title = 'Export',\n endpoint,\n canExport,\n disabledReason,\n description = 'A zip of everything. Credentials are never included.',\n fetcher,\n continuesInBackground = false,\n children,\n}: ExportSectionProps) {\n const { latest, error, loading, starting, running, start, retry } = useWorkspaceExport({ endpoint, enabled: canExport, fetcher })\n const showExport = canExport || disabledReason != null\n if (!showExport && children == null) return null\n return (\n <SettingsSection id={id} title={title}>\n {showExport && (\n <SettingsRow\n label=\"Export everything\"\n layout=\"compact\"\n description={description}\n disabledReason={canExport ? undefined : disabledReason}\n error={canExport ? error ?? undefined : undefined}\n onRetry={canExport && error ? retry : undefined}\n >\n <Button type=\"button\" variant=\"outline\" disabled={!canExport || loading || starting || running} loading={starting} onClick={() => void start()}>\n {running ? 'Exporting…' : 'Export'}\n </Button>\n </SettingsRow>\n )}\n {canExport && latest && <LatestExport latest={latest} continuesInBackground={continuesInBackground} />}\n {children}\n </SettingsSection>\n )\n}\n","/**\n * The danger card at the bottom of a settings page. At rest it is an outline\n * destructive button, never the loudest control on the page; the filled\n * destructive button is inside the dialog, which counts what goes, asks for\n * the name and navigates only after a confirmed delete.\n */\n\nimport { Button, SettingsRow, SettingsSection } from '@tangle-network/ui/primitives'\nimport type { ReactNode } from 'react'\nimport type { MutationOutcome } from '../web-react/async'\nimport { ConfirmAction } from './confirm-action'\n\n/** One thing the delete removes or stops, such as `{ count: 12, label: 'chats' }`. */\nexport interface DeleteCount {\n count: number\n label: string\n}\n\nexport interface DeleteWorkspaceSectionProps {\n /** The card's anchor. */\n id?: string\n /** The workspace's name; the person types it to confirm. */\n name: string\n /** What the delete removes or stops, counted. Zero counts are left out. */\n counts?: DeleteCount[]\n /** The product's word for a workspace, as in \"Delete project\". */\n noun?: string\n /** Why the viewer cannot delete, naming who can. */\n disabledReason?: ReactNode\n /** A sentence pointing to an export, shown in the dialog. */\n notice?: ReactNode\n onDelete: () => Promise<MutationOutcome<unknown>>\n /** Runs only after a confirmed delete: navigate away here. */\n onDeleted: () => void\n}\n\nfunction listCounts(counts: DeleteCount[]): string | null {\n const shown = counts.filter((entry) => entry.count > 0).map((entry) => `${entry.count.toLocaleString()} ${entry.label}`)\n if (shown.length === 0) return null\n return shown.length === 1 ? shown[0]! : `${shown.slice(0, -1).join(', ')} and ${shown[shown.length - 1]}`\n}\n\nexport function DeleteWorkspaceSection({\n id = 'delete',\n name,\n counts = [],\n noun = 'project',\n disabledReason,\n notice,\n onDelete,\n onDeleted,\n}: DeleteWorkspaceSectionProps) {\n const title = `Delete ${noun}`\n const removed = listCounts(counts)\n const trigger = (\n <Button type=\"button\" variant=\"destructive-outline\" disabled={disabledReason != null}>\n {title}…\n </Button>\n )\n return (\n <SettingsSection id={id} title={title} tone=\"danger\">\n <SettingsRow\n label={`Delete this ${noun} for everyone`}\n description=\"This can’t be undone.\"\n disabledReason={disabledReason}\n layout=\"compact\"\n >\n {disabledReason != null ? trigger : (\n <ConfirmAction\n trigger={trigger}\n title={`Delete ${name}?`}\n consequence={removed\n ? `This permanently deletes ${name} for everyone: ${removed}. It can’t be undone.`\n : `This permanently deletes ${name} for everyone. It can’t be undone.`}\n notice={notice}\n confirmLabel={title}\n tone=\"danger\"\n requireText={name}\n onConfirm={onDelete}\n onConfirmed={onDeleted}\n />\n )}\n </SettingsRow>\n </SettingsSection>\n )\n}\n","/**\n * A searchable timezone field: the same popover, search and keyboard model as\n * `ModelPicker`. Zones come from the browser's IANA list, grouped by region\n * with today's UTC offset, and match by city, zone name or offset (\"+5:30\",\n * \"utc-4\"). The value is always the IANA name; one the list lacks still shows.\n */\n\nimport { Button, Input } from '@tangle-network/ui/primitives'\nimport { Check, ChevronDown } from 'lucide-react'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport { fieldPickerTriggerClass, OVERLAY_SHADOW, POPOVER_OPTION_FOCUS, PopoverSurface, usePopover } from '../web-react/controls'\n\nexport interface TimezonePickerProps {\n /** The trigger's id, for the row's label. */\n id?: string\n /** An IANA zone name, such as `America/New_York`. */\n value: string\n onChange: (zone: string) => void\n disabled?: boolean\n 'aria-describedby'?: string\n 'aria-invalid'?: boolean\n}\n\ninterface Zone {\n id: string\n /** The name the browser's list used, when it is an older spelling of `id`. */\n legacy?: string\n city: string\n region: string\n offsetMinutes: number\n offsetLabel: string\n}\n\nconst REGIONS: Array<[prefix: string, label: string]> = [\n ['America', 'Americas'],\n ['Europe', 'Europe'],\n ['Africa', 'Africa'],\n ['Asia', 'Asia'],\n ['Australia', 'Australia'],\n ['Pacific', 'Pacific'],\n ['Atlantic', 'Atlantic'],\n ['Indian', 'Indian Ocean'],\n ['Antarctica', 'Antarctica'],\n ['Arctic', 'Arctic'],\n]\nconst OTHER_REGION = 'UTC and other'\n\nfunction regionOf(id: string): string {\n const prefix = id.split('/')[0] ?? ''\n return REGIONS.find(([name]) => name === prefix)?.[1] ?? OTHER_REGION\n}\n\nfunction cityOf(id: string): string {\n if (id === 'UTC' || id === 'Etc/UTC') return 'Coordinated Universal Time'\n return (id.split('/').pop() ?? id).replace(/_/g, ' ')\n}\n\nfunction offsetMinutesOf(id: string, now: Date): number | null {\n try {\n const part = new Intl.DateTimeFormat('en-US', { timeZone: id, timeZoneName: 'longOffset' })\n .formatToParts(now).find((p) => p.type === 'timeZoneName')?.value ?? ''\n const match = /^GMT(?:([+-−])(\\d{1,2})(?::(\\d{2}))?)?$/.exec(part)\n if (!match) return null\n if (!match[1]) return 0\n const minutes = Number(match[2]) * 60 + Number(match[3] ?? 0)\n return match[1] === '+' ? minutes : -minutes\n } catch {\n return null\n }\n}\n\nfunction offsetLabel(minutes: number): string {\n if (minutes === 0) return 'UTC'\n const sign = minutes > 0 ? '+' : '−'\n const abs = Math.abs(minutes)\n const rest = abs % 60\n return `UTC${sign}${Math.floor(abs / 60)}${rest ? `:${String(rest).padStart(2, '0')}` : ''}`\n}\n\n/**\n * Browsers list some zones under names the IANA database has since replaced\n * (Chromium lists `Asia/Calcutta`). The picker shows and saves the current\n * name, so a search for \"Kolkata\" or \"Kyiv\" finds its city; the old spelling\n * still matches.\n */\nconst CURRENT_NAME: Record<string, string> = {\n 'America/Buenos_Aires': 'America/Argentina/Buenos_Aires',\n 'America/Catamarca': 'America/Argentina/Catamarca',\n 'America/Cordoba': 'America/Argentina/Cordoba',\n 'America/Godthab': 'America/Nuuk',\n 'America/Indianapolis': 'America/Indiana/Indianapolis',\n 'America/Jujuy': 'America/Argentina/Jujuy',\n 'America/Louisville': 'America/Kentucky/Louisville',\n 'America/Mendoza': 'America/Argentina/Mendoza',\n 'Asia/Calcutta': 'Asia/Kolkata',\n 'Asia/Katmandu': 'Asia/Kathmandu',\n 'Asia/Rangoon': 'Asia/Yangon',\n 'Asia/Saigon': 'Asia/Ho_Chi_Minh',\n 'Atlantic/Faeroe': 'Atlantic/Faroe',\n 'Europe/Kiev': 'Europe/Kyiv',\n 'Pacific/Enderbury': 'Pacific/Kanton',\n 'Pacific/Ponape': 'Pacific/Pohnpei',\n 'Pacific/Truk': 'Pacific/Chuuk',\n}\n\nfunction zoneOf(id: string, now: Date, legacy?: string): Zone | null {\n const offset = offsetMinutesOf(id, now)\n if (offset === null) return null\n return { id, ...(legacy ? { legacy } : {}), city: cityOf(id), region: regionOf(id), offsetMinutes: offset, offsetLabel: offsetLabel(offset) }\n}\n\nfunction allZones(now: Date): Zone[] {\n const listed = typeof Intl.supportedValuesOf === 'function' ? Intl.supportedValuesOf('timeZone') : []\n const zones = new Map<string, Zone>()\n for (const name of listed.includes('UTC') ? listed : [...listed, 'UTC']) {\n const id = CURRENT_NAME[name] ?? name\n if (zones.has(id)) continue\n const zone = zoneOf(id, now, id === name ? undefined : name) ?? zoneOf(name, now)\n if (zone) zones.set(zone.id, zone)\n }\n return [...zones.values()]\n}\n\n/** \"+5:30\", \"utc-4\", \"gmt +2\" and \"−03\" read as an offset in minutes. */\nfunction queryOffset(query: string): number | null {\n const match = /^(?:utc|gmt)?\\s*([+\\-−])\\s*(\\d{1,2})(?::?(\\d{2}))?$/.exec(query)\n if (!match) return /^(?:utc|gmt)$/.test(query) ? 0 : null\n const minutes = Number(match[2]) * 60 + Number(match[3] ?? 0)\n return match[1] === '+' ? minutes : -minutes\n}\n\nfunction matches(zone: Zone, query: string): boolean {\n const offset = queryOffset(query)\n if (offset !== null) return zone.offsetMinutes === offset\n return zone.city.toLowerCase().includes(query)\n || zone.id.toLowerCase().replace(/_/g, ' ').includes(query)\n || (zone.legacy?.toLowerCase().replace(/_/g, ' ').includes(query) ?? false)\n || zone.region.toLowerCase().includes(query)\n}\n\nfunction grouped(zones: Zone[]): Array<{ region: string; zones: Zone[] }> {\n const order = [...REGIONS.map(([, label]) => label), OTHER_REGION]\n return order\n .map((region) => ({\n region,\n zones: zones.filter((zone) => zone.region === region)\n .sort((a, b) => a.offsetMinutes - b.offsetMinutes || a.city.localeCompare(b.city)),\n }))\n .filter((group) => group.zones.length > 0)\n}\n\nexport function TimezonePicker({ id, value, onChange, disabled = false, 'aria-describedby': describedBy, 'aria-invalid': invalid }: TimezonePickerProps) {\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n const expanded = open && !disabled\n const changeOpen = useCallback((next: boolean) => {\n setOpen(next && !disabled)\n if (!next) setQuery('')\n }, [disabled])\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(expanded, changeOpen)\n const inputRef = useRef<HTMLInputElement>(null)\n const panelId = useId()\n const searchId = useId()\n // Offsets are today's, so a zone in daylight time shows its current offset.\n const now = useMemo(() => new Date(), [expanded])\n const selected = useMemo(() => zoneOf(CURRENT_NAME[value] ?? value, new Date()), [value])\n const zones = useMemo(() => (expanded ? allZones(now) : []), [expanded, now])\n const q = query.trim().toLowerCase()\n const groups = useMemo(() => grouped(q ? zones.filter((zone) => matches(zone, q)) : zones), [zones, q])\n\n useEffect(() => {\n if (!expanded) return\n const focus = () => inputRef.current?.focus()\n focus()\n const frame = requestAnimationFrame(focus)\n return () => cancelAnimationFrame(frame)\n }, [expanded])\n\n useEffect(() => {\n const panel = panelRef.current\n if (!expanded || !panel) return\n function onKeyDown(event: KeyboardEvent) {\n if (event.defaultPrevented || event.isComposing || event.altKey || event.ctrlKey || event.metaKey) return\n const input = inputRef.current\n if (!input || !panel) return\n const controls: HTMLElement[] = [input, ...panel.querySelectorAll<HTMLButtonElement>('button[data-zone]')]\n const index = controls.indexOf(document.activeElement as HTMLElement)\n if (index < 0) return\n if (event.key === 'Tab') {\n if ((event.shiftKey && index === 0) || (!event.shiftKey && index === controls.length - 1)) {\n changeOpen(false)\n triggerRef.current?.focus()\n if (event.shiftKey) event.preventDefault()\n }\n return\n }\n let next: number\n if (event.key === 'ArrowDown') next = (index + 1) % controls.length\n else if (event.key === 'ArrowUp') next = (index - 1 + controls.length) % controls.length\n else if (event.key === 'Enter' && index === 0 && controls.length > 1) {\n event.preventDefault()\n ;(controls[1] as HTMLButtonElement).click()\n return\n } else return\n event.preventDefault()\n controls[next]?.focus()\n }\n panel.addEventListener('keydown', onKeyDown)\n return () => panel.removeEventListener('keydown', onKeyDown)\n })\n\n const pick = (zone: string) => {\n changeOpen(false)\n triggerRef.current?.focus()\n if (zone !== value) onChange(zone)\n }\n\n return (\n <div ref={containerRef} className=\"relative flex w-full min-w-0\">\n <Button\n type=\"button\"\n variant=\"bare\"\n {...triggerProps}\n id={id}\n disabled={disabled}\n aria-haspopup=\"dialog\"\n aria-controls={expanded ? panelId : undefined}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n data-state={expanded ? 'open' : 'closed'}\n onClick={() => changeOpen(!expanded)}\n onKeyDown={(event) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n changeOpen(true)\n }\n }}\n className={fieldPickerTriggerClass({ interactive: !disabled })}\n >\n <span className=\"flex min-w-0 flex-1 items-center gap-2\">\n <span className=\"min-w-0 truncate\">{selected?.city ?? (value || 'Choose a timezone')}</span>\n {selected && <span className=\"shrink-0 text-muted-foreground tabular-nums\">{selected.offsetLabel}</span>}\n </span>\n <ChevronDown aria-hidden=\"true\" className=\"size-4 shrink-0 text-muted-foreground\" />\n </Button>\n\n <PopoverSurface\n open={expanded}\n id={panelId}\n role=\"dialog\"\n aria-label=\"Choose a timezone\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n matchTriggerWidth\n side=\"below\"\n contentKey={q}\n className={`flex w-[22rem] max-w-[calc(100vw-2rem)] flex-col overflow-hidden rounded-xl border border-card-edge bg-popover text-foreground ${OVERLAY_SHADOW}`}\n >\n <div className=\"shrink-0 border-b border-border p-2\">\n <Input\n ref={inputRef}\n id={searchId}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search by city or UTC offset\"\n aria-label=\"Search timezones\"\n autoComplete=\"off\"\n spellCheck={false}\n />\n </div>\n <div className=\"min-h-0 overflow-y-auto p-1 pb-2\" data-zone-list>\n {groups.length === 0 && (\n <p role=\"status\" className=\"px-3 py-4 text-center text-sm text-muted-foreground\">No timezone matches “{query.trim()}”</p>\n )}\n {groups.map((group) => (\n <div key={group.region} role=\"group\" aria-label={group.region}>\n <p className=\"px-3 pb-1 pt-3 text-sm font-semibold text-muted-foreground\">{group.region}</p>\n {group.zones.map((zone) => {\n const current = zone.id === (CURRENT_NAME[value] ?? value)\n return (\n <Button\n key={zone.id}\n type=\"button\"\n variant=\"bare\"\n data-zone={zone.id}\n aria-pressed={current}\n title={zone.id}\n onClick={() => pick(zone.id)}\n className={`flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-sm ${POPOVER_OPTION_FOCUS}`}\n >\n <span className=\"min-w-0 flex-1 truncate\">{zone.city}</span>\n <span className=\"shrink-0 text-muted-foreground tabular-nums\">{zone.offsetLabel}</span>\n <Check aria-hidden=\"true\" className={`size-4 shrink-0 text-[var(--accent-text)] ${current ? '' : 'invisible'}`} />\n </Button>\n )\n })}\n </div>\n ))}\n </div>\n </PopoverSurface>\n </div>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs","useCallback","useEffect","useRef","useState","jsx","useState","useRef","useEffect","useCallback","Button","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","useEffect","useRef","useCallback","Button","Button","CircleAlert","useEffect","useState","jsx","jsxs","useState","useEffect","CircleAlert","Button","Button","Input","Label","Fragment","useEffect","useId","useState","Fragment","jsx","jsxs","useState","useId","useEffect","Label","Input","Button","Button","Collapsible","CollapsibleContent","CollapsibleTrigger","SettingsRow","SettingsSection","ChevronDown","useState","Fragment","jsx","jsxs","useState","SettingsRow","Button","Collapsible","CollapsibleTrigger","ChevronDown","CollapsibleContent","SettingsSection","Button","SettingsRow","SettingsSection","jsx","jsxs","Button","SettingsSection","SettingsRow","Button","Input","Check","ChevronDown","useCallback","useEffect","useId","useMemo","useRef","useState","jsx","jsxs","useState","useCallback","useRef","useId","useMemo","useEffect","Button","ChevronDown","Input","Check"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who has access and what each person can do, as settings cards built on the
|
|
3
|
+
* teams panels' contracts and rules: the same `MemberView` and
|
|
4
|
+
* `InvitationView` rows, the same role ladder and invite check, and roles
|
|
5
|
+
* named by capability. A role change saves at once and says so in its row;
|
|
6
|
+
* removing a person or revoking an invitation asks first and stays open on a
|
|
7
|
+
* refusal. On a narrow card the role picker takes its own full-width line
|
|
8
|
+
* under the name.
|
|
9
|
+
*/
|
|
10
|
+
import { type SettingsListMenuItem } from '@tangle-network/ui/primitives';
|
|
11
|
+
import { type ReactNode } from 'react';
|
|
12
|
+
import { type WorkspaceRole } from '../teams/roles';
|
|
13
|
+
import type { InvitationView, MemberView } from '../teams-react/contracts';
|
|
14
|
+
import { type MutationOutcome } from '../web-react/async';
|
|
15
|
+
import { type SettingsLinkComponent } from './settings-layout';
|
|
16
|
+
export interface MembersSectionProps {
|
|
17
|
+
/** The card's anchor. */
|
|
18
|
+
id?: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
/** One line under the title, such as the seat count. */
|
|
21
|
+
description?: ReactNode;
|
|
22
|
+
members: MemberView[];
|
|
23
|
+
/** The viewer's effective role: admins change roles, remove people and invite. */
|
|
24
|
+
currentRole: WorkspaceRole;
|
|
25
|
+
/** The viewer's user id, so their own row offers no role change or removal. */
|
|
26
|
+
currentUserId?: string | null;
|
|
27
|
+
/** The noun people lose access to, as in "lose access to this project". */
|
|
28
|
+
noun?: string;
|
|
29
|
+
onChangeRole(input: {
|
|
30
|
+
memberId: string;
|
|
31
|
+
role: WorkspaceRole;
|
|
32
|
+
}): Promise<MutationOutcome<unknown>>;
|
|
33
|
+
onRemove(input: {
|
|
34
|
+
memberId: string;
|
|
35
|
+
}): Promise<MutationOutcome<unknown>>;
|
|
36
|
+
/** Adds the Invite action. A refusal (a seat limit, a failed email) stays in the dialog with its sentence. */
|
|
37
|
+
onInvite?(input: {
|
|
38
|
+
email: string;
|
|
39
|
+
role: WorkspaceRole;
|
|
40
|
+
}): Promise<MutationOutcome<unknown>>;
|
|
41
|
+
/** The plans page. A `plan_limit` refusal on Invite links to it as View plans. */
|
|
42
|
+
plansHref?: string;
|
|
43
|
+
/** Renders View plans: pass the router's link, as to `SettingsLayout`. Defaults to a plain anchor. */
|
|
44
|
+
LinkComponent?: SettingsLinkComponent;
|
|
45
|
+
/** More ⋯ items for a member the viewer can manage, such as "Make team owner…". */
|
|
46
|
+
menuItems?(member: MemberView): SettingsListMenuItem[];
|
|
47
|
+
}
|
|
48
|
+
export declare function MembersSection({ id, title, description, members, currentRole, currentUserId, noun, onChangeRole, onRemove, onInvite, plansHref, LinkComponent, menuItems, }: MembersSectionProps): import("react").JSX.Element;
|
|
49
|
+
export interface InvitationsSectionProps {
|
|
50
|
+
id?: string;
|
|
51
|
+
title?: string;
|
|
52
|
+
invitations: InvitationView[];
|
|
53
|
+
/** The viewer's effective role: admins copy, resend and revoke. */
|
|
54
|
+
currentRole: WorkspaceRole;
|
|
55
|
+
onResend(input: {
|
|
56
|
+
invitationId: string;
|
|
57
|
+
}): Promise<MutationOutcome<unknown>>;
|
|
58
|
+
onRevoke(input: {
|
|
59
|
+
invitationId: string;
|
|
60
|
+
}): Promise<MutationOutcome<unknown>>;
|
|
61
|
+
/** Copies the invite link; defaults to `navigator.clipboard.writeText`. */
|
|
62
|
+
onCopy?(input: {
|
|
63
|
+
inviteUrl: string;
|
|
64
|
+
}): void | Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export declare function InvitationsSection({ id, title, invitations, currentRole, onResend, onRevoke, onCopy, }: InvitationsSectionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The words a settings row shows after its label while a write is in flight
|
|
3
|
+
* or just landed. One size (14px) and no colour at rest: "Saved" is the only
|
|
4
|
+
* green, and it fades on its own.
|
|
5
|
+
*
|
|
6
|
+
* `MutationStatus` (web-react/async) is the status line for a write with no
|
|
7
|
+
* settings row: it shows the moment a write starts, keeps "Saved" until the
|
|
8
|
+
* next write, and prints the refusal itself. A row's status instead waits out
|
|
9
|
+
* the pending delay, fades, and leaves the refusal to the row's error slot,
|
|
10
|
+
* so its words are drawn here.
|
|
11
|
+
*/
|
|
12
|
+
/** How long "Saving…" waits before it shows (`--interaction-pending-delay`), so a fast write shows only "Saved". */
|
|
13
|
+
export declare const PENDING_DELAY_MS = 200;
|
|
14
|
+
/** How long "Saved" stays after a confirmed write. */
|
|
15
|
+
export declare const SAVED_VISIBLE_MS = 2000;
|
|
16
|
+
export declare function SavingStatus({ label }: {
|
|
17
|
+
label?: string;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
19
|
+
export declare function SavedStatus({ label }: {
|
|
20
|
+
label?: string;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
22
|
+
export declare function UnsavedStatus({ label }: {
|
|
23
|
+
label?: string;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed outcomes for a settings write. A settings API answers a refusal with
|
|
3
|
+
* `{ error: { code, message, field?, retryAfterSeconds? } }`; this reads that
|
|
4
|
+
* body into a rejection whose message is the server's sentence, so the row
|
|
5
|
+
* shows "Only the owner can turn sources on" instead of "Request failed (403)".
|
|
6
|
+
* A request that never reached the server is the `network` code.
|
|
7
|
+
*/
|
|
8
|
+
import { type MutationOutcome, type MutationRejected } from '../web-react/async';
|
|
9
|
+
/** The refusal codes settings routes share. A product may send others; they keep the server's message. */
|
|
10
|
+
export type SettingFailureCode = 'forbidden' | 'invalid' | 'plan_limit' | 'rate_limited' | 'not_runnable' | 'upstream' | 'network';
|
|
11
|
+
export interface SettingFailure {
|
|
12
|
+
code: SettingFailureCode | (string & {});
|
|
13
|
+
/** The sentence the row shows. */
|
|
14
|
+
message: string;
|
|
15
|
+
/** The field an `invalid` refusal names. */
|
|
16
|
+
field?: string;
|
|
17
|
+
/** When a `rate_limited` refusal can be tried again. */
|
|
18
|
+
retryAfterSeconds?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Confirms a settings write from its response, or rejects it with the
|
|
22
|
+
* server's typed failure. Pass the `fetch` promise itself so a request that
|
|
23
|
+
* never reached the server becomes a `network` failure rather than a throw.
|
|
24
|
+
* `parse` returns the value the server kept: read it from the 2xx body when
|
|
25
|
+
* the route answers with the saved record, or return the value sent
|
|
26
|
+
* (`() => next`) when it answers `{ success: true }` or 204. Without `parse`
|
|
27
|
+
* the value is the body, typed `unknown`, so it cannot stand in for a field's
|
|
28
|
+
* value.
|
|
29
|
+
*/
|
|
30
|
+
export declare function confirmSettingResponse(request: Response | Promise<Response>): Promise<MutationOutcome<unknown>>;
|
|
31
|
+
export declare function confirmSettingResponse<T>(request: Response | Promise<Response>, parse: (data: unknown) => T): Promise<MutationOutcome<T>>;
|
|
32
|
+
/** The typed failure behind a rejected settings write (or a mutation's `failed` state), when it carries one. */
|
|
33
|
+
export declare function settingFailure(outcome: Pick<MutationRejected, 'message' | 'error'>): SettingFailure;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The settings page: a `PageHeader`, one link per section and the current
|
|
3
|
+
* section's cards, in `PageShell measure="settings"`. Each section is its own
|
|
4
|
+
* URL, so a product routes `/settings/<section>` and loads only that
|
|
5
|
+
* section's data. The frame answers to the content area's width: from 60rem
|
|
6
|
+
* the links are a sticky 13rem column beside a 48rem column of cards, and
|
|
7
|
+
* below that a sticky 56px tab strip whose current tab is kept in view.
|
|
8
|
+
*
|
|
9
|
+
* Navigation responds at once: the clicked link becomes current before the
|
|
10
|
+
* route resolves, nothing dims, and a thin progress line shows only after the
|
|
11
|
+
* pending delay. A `#hash` scrolls its section (or row) into view and focuses
|
|
12
|
+
* its heading.
|
|
13
|
+
*/
|
|
14
|
+
import { type AnchorHTMLAttributes, type ComponentType, type ReactNode } from 'react';
|
|
15
|
+
/** An icon component such as a `lucide-react` icon. It shows only in the column. */
|
|
16
|
+
export type SettingsIcon = ComponentType<{
|
|
17
|
+
className?: string;
|
|
18
|
+
'aria-hidden'?: boolean | 'true' | 'false';
|
|
19
|
+
}>;
|
|
20
|
+
export interface SettingsNavSection {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
/** The section's own URL. */
|
|
24
|
+
href: string;
|
|
25
|
+
icon?: SettingsIcon;
|
|
26
|
+
/** Leaves the link out, for a section the viewer's role cannot open. */
|
|
27
|
+
hidden?: boolean;
|
|
28
|
+
/** A short count after the label, such as the number of members. */
|
|
29
|
+
badge?: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
/** What a `LinkComponent` receives: render an anchor (or a router link) with these props. */
|
|
32
|
+
export interface SettingsLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
33
|
+
href: string;
|
|
34
|
+
children?: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
export type SettingsLinkComponent = ComponentType<SettingsLinkProps>;
|
|
37
|
+
export interface SettingsNavProps {
|
|
38
|
+
sections: SettingsNavSection[];
|
|
39
|
+
/** The section the route rendered. */
|
|
40
|
+
activeId: string;
|
|
41
|
+
/**
|
|
42
|
+
* Renders each link: pass the router's link (with prefetch off where the
|
|
43
|
+
* edge refuses prefetch requests). Defaults to a plain anchor.
|
|
44
|
+
*/
|
|
45
|
+
LinkComponent?: SettingsLinkComponent;
|
|
46
|
+
/** Called once per section when a pointer rests on or focus reaches its link: warm its route module here. */
|
|
47
|
+
onIntent?: (section: SettingsNavSection) => void;
|
|
48
|
+
}
|
|
49
|
+
export interface SettingsLayoutProps extends SettingsNavProps {
|
|
50
|
+
/** The page title, such as "Project settings". */
|
|
51
|
+
title: ReactNode;
|
|
52
|
+
/** One line under the title, such as the workspace name. */
|
|
53
|
+
description?: ReactNode;
|
|
54
|
+
/** A second line under the description, such as "Setup in progress · Finish setup". */
|
|
55
|
+
meta?: ReactNode;
|
|
56
|
+
/** The current section's `SettingsSection` cards. */
|
|
57
|
+
children: ReactNode;
|
|
58
|
+
/** The navigation landmark's name. */
|
|
59
|
+
navigationLabel?: string;
|
|
60
|
+
/** Bounds the frame the layout answers to, such as `mx-auto max-w-5xl`. */
|
|
61
|
+
className?: string;
|
|
62
|
+
}
|
|
63
|
+
/** The default `LinkComponent`: a plain anchor, so a click is a document load. */
|
|
64
|
+
export declare function PlainLink({ href, children, ...rest }: SettingsLinkProps): import("react").JSX.Element;
|
|
65
|
+
/**
|
|
66
|
+
* The section links alone, for a page that composes its own
|
|
67
|
+
* `PageShell measure="settings"`: pass it as the shell's `navigation`, which
|
|
68
|
+
* draws the column, the tab strip and the current link.
|
|
69
|
+
*/
|
|
70
|
+
export declare function SettingsNav(props: SettingsNavProps): import("react").JSX.Element;
|
|
71
|
+
export declare function SettingsLayout({ title, description, meta, sections, activeId, LinkComponent, onIntent, navigationLabel, className, children, }: SettingsLayoutProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one save bar a group of settings may have, for values that only make
|
|
3
|
+
* sense saved together (a set of spend caps). Pass it as a `SettingsSection`
|
|
4
|
+
* footer. Save is the primary action only while there are changes; a refusal
|
|
5
|
+
* shows its sentence here and the edits stay. "Saved" shows for two seconds
|
|
6
|
+
* after `state` becomes `saved`, as a row's does. On a narrow card with
|
|
7
|
+
* unsaved changes the footer sticks to the bottom of the screen, so Save stays
|
|
8
|
+
* in reach while the person scrolls the fields above it.
|
|
9
|
+
*/
|
|
10
|
+
import { type ReactNode } from 'react';
|
|
11
|
+
export interface SettingsSaveBarProps {
|
|
12
|
+
/** How many values differ from the saved ones. */
|
|
13
|
+
changes: number;
|
|
14
|
+
/** Replaces the "2 unsaved changes" count. */
|
|
15
|
+
summary?: ReactNode;
|
|
16
|
+
onDiscard: () => void;
|
|
17
|
+
onSave: () => void;
|
|
18
|
+
state: 'idle' | 'saving' | 'saved' | 'failed';
|
|
19
|
+
/** The refusal to show while `state` is `failed`. */
|
|
20
|
+
error?: ReactNode;
|
|
21
|
+
/** The verb on the save button. */
|
|
22
|
+
saveLabel?: string;
|
|
23
|
+
/** Why the person cannot save, naming who can. Replaces the buttons' availability. */
|
|
24
|
+
disabledReason?: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
export declare function SettingsSaveBar({ changes, summary, onDiscard, onSave, state, error, saveLabel, disabledReason, }: SettingsSaveBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A searchable timezone field: the same popover, search and keyboard model as
|
|
3
|
+
* `ModelPicker`. Zones come from the browser's IANA list, grouped by region
|
|
4
|
+
* with today's UTC offset, and match by city, zone name or offset ("+5:30",
|
|
5
|
+
* "utc-4"). The value is always the IANA name; one the list lacks still shows.
|
|
6
|
+
*/
|
|
7
|
+
export interface TimezonePickerProps {
|
|
8
|
+
/** The trigger's id, for the row's label. */
|
|
9
|
+
id?: string;
|
|
10
|
+
/** An IANA zone name, such as `America/New_York`. */
|
|
11
|
+
value: string;
|
|
12
|
+
onChange: (zone: string) => void;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
'aria-describedby'?: string;
|
|
15
|
+
'aria-invalid'?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function TimezonePicker({ id, value, onChange, disabled, 'aria-describedby': describedBy, 'aria-invalid': invalid }: TimezonePickerProps): import("react").JSX.Element;
|