@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.
Files changed (83) hide show
  1. package/dist/agent-profiles/d1.js +3 -3
  2. package/dist/agent-profiles/index.js +3 -3
  3. package/dist/assistant/index.js +9 -9
  4. package/dist/channels/index.js +1 -1
  5. package/dist/chat-react/index.js +4 -4
  6. package/dist/chat-routes/index.js +4 -4
  7. package/dist/{chunk-YXLN4PXK.js → chunk-2RCPPOQ4.js} +2 -2
  8. package/dist/{chunk-YESFGM56.js → chunk-5IS3GCMR.js} +3 -3
  9. package/dist/{chunk-3RHAF2P5.js → chunk-A3CUVYRZ.js} +3 -3
  10. package/dist/{chunk-LN5RBVGP.js → chunk-AVP5SNIG.js} +4 -4
  11. package/dist/{chunk-LN5RBVGP.js.map → chunk-AVP5SNIG.js.map} +1 -1
  12. package/dist/{chunk-7BYY7QKC.js → chunk-E65YV7VV.js} +5 -5
  13. package/dist/{chunk-7BYY7QKC.js.map → chunk-E65YV7VV.js.map} +1 -1
  14. package/dist/chunk-EZFBGW23.js +179 -0
  15. package/dist/chunk-EZFBGW23.js.map +1 -0
  16. package/dist/{chunk-UDLA3NGY.js → chunk-GNBBY3KN.js} +3 -3
  17. package/dist/{chunk-KOTNMTHV.js → chunk-HCFVEJQL.js} +3 -3
  18. package/dist/{chunk-YGBEGBJY.js → chunk-HEPEGY6Q.js} +2 -2
  19. package/dist/{chunk-AJZ3EJB5.js → chunk-IJAXOQJU.js} +2 -2
  20. package/dist/{chunk-EQRVOMMS.js → chunk-KMIDRUUH.js} +2 -2
  21. package/dist/{chunk-BBHVV37T.js → chunk-MSAGTFN4.js} +4 -4
  22. package/dist/chunk-OBKVNZPH.js +70 -0
  23. package/dist/chunk-OBKVNZPH.js.map +1 -0
  24. package/dist/{chunk-75ZIWV4U.js → chunk-QAF7ZTO3.js} +4 -4
  25. package/dist/{chunk-TJ4DYKF2.js → chunk-XG6FODYO.js} +3 -2
  26. package/dist/chunk-XG6FODYO.js.map +1 -0
  27. package/dist/{chunk-6O2NQ7WD.js → chunk-Y2SOQOJE.js} +3 -3
  28. package/dist/integrations-react/index.js +1 -1
  29. package/dist/{mention-editor-ZJAOUDEE.js → mention-editor-NOS5R3AU.js} +2 -2
  30. package/dist/preset-cloudflare/index.js +2 -2
  31. package/dist/sandbox/index.js +4 -4
  32. package/dist/settings-react/confirm-action.d.ts +51 -0
  33. package/dist/settings-react/delete-workspace-section.d.ts +31 -0
  34. package/dist/settings-react/export-section.d.ts +26 -0
  35. package/dist/settings-react/index.d.ts +20 -0
  36. package/dist/settings-react/index.js +1438 -0
  37. package/dist/settings-react/index.js.map +1 -0
  38. package/dist/settings-react/members-section.d.ts +66 -0
  39. package/dist/settings-react/save-status.d.ts +24 -0
  40. package/dist/settings-react/setting-outcome.d.ts +33 -0
  41. package/dist/settings-react/settings-layout.d.ts +71 -0
  42. package/dist/settings-react/settings-save-bar.d.ts +26 -0
  43. package/dist/settings-react/timezone-picker.d.ts +17 -0
  44. package/dist/settings-react/use-setting-field.d.ts +70 -0
  45. package/dist/stories/settings/settings-backend.d.ts +66 -0
  46. package/dist/stories/settings/settings-demo.d.ts +16 -0
  47. package/dist/stories/settings/team-panels-demo.d.ts +21 -0
  48. package/dist/studio-react/index.js +2 -2
  49. package/dist/teams-react/components/InviteForm.d.ts +2 -2
  50. package/dist/teams-react/components/MembersPanel.d.ts +9 -5
  51. package/dist/teams-react/components/RoleSelect.d.ts +10 -8
  52. package/dist/teams-react/components/invitation-detail.d.ts +8 -0
  53. package/dist/teams-react/components/member-access.d.ts +17 -0
  54. package/dist/teams-react/contracts.d.ts +2 -0
  55. package/dist/teams-react/index.js +119 -242
  56. package/dist/teams-react/index.js.map +1 -1
  57. package/dist/theme/tokens.css +43 -2
  58. package/dist/tools/index.js +4 -4
  59. package/dist/web-react/agent-profile-editor.js +4 -4
  60. package/dist/web-react/async/index.js +1 -1
  61. package/dist/web-react/controls.d.ts +4 -5
  62. package/dist/web-react/index.js +12 -12
  63. package/dist/work-product/index.js +2 -2
  64. package/dist/work-product-react/index.js +3 -3
  65. package/dist/work-product-react/index.js.map +1 -1
  66. package/dist/workspace-export/react.d.ts +26 -0
  67. package/dist/workspace-export/react.js +6 -169
  68. package/dist/workspace-export/react.js.map +1 -1
  69. package/dist/workspace-react/index.js +3 -3
  70. package/package.json +11 -6
  71. package/dist/chunk-TJ4DYKF2.js.map +0 -1
  72. /package/dist/{chunk-YXLN4PXK.js.map → chunk-2RCPPOQ4.js.map} +0 -0
  73. /package/dist/{chunk-YESFGM56.js.map → chunk-5IS3GCMR.js.map} +0 -0
  74. /package/dist/{chunk-3RHAF2P5.js.map → chunk-A3CUVYRZ.js.map} +0 -0
  75. /package/dist/{chunk-UDLA3NGY.js.map → chunk-GNBBY3KN.js.map} +0 -0
  76. /package/dist/{chunk-KOTNMTHV.js.map → chunk-HCFVEJQL.js.map} +0 -0
  77. /package/dist/{chunk-YGBEGBJY.js.map → chunk-HEPEGY6Q.js.map} +0 -0
  78. /package/dist/{chunk-AJZ3EJB5.js.map → chunk-IJAXOQJU.js.map} +0 -0
  79. /package/dist/{chunk-EQRVOMMS.js.map → chunk-KMIDRUUH.js.map} +0 -0
  80. /package/dist/{chunk-BBHVV37T.js.map → chunk-MSAGTFN4.js.map} +0 -0
  81. /package/dist/{chunk-75ZIWV4U.js.map → chunk-QAF7ZTO3.js.map} +0 -0
  82. /package/dist/{chunk-6O2NQ7WD.js.map → chunk-Y2SOQOJE.js.map} +0 -0
  83. /package/dist/{mention-editor-ZJAOUDEE.js.map → mention-editor-NOS5R3AU.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/teams-react/components/InviteForm.tsx","../../src/teams-react/components/RoleSelect.tsx","../../src/teams-react/components/MembersPanel.tsx","../../src/teams-react/components/InvitationsPanel.tsx","../../src/teams-react/components/InviteAcceptPage.tsx","../../src/teams-react/components/BrandMark.tsx"],"sourcesContent":["/**\n * The invite-by-email row both team panels share: address, role, Invite. The\n * address is checked with the same rule the invitations API enforces, so\n * Invite stays disabled until the address could be delivered to, and the\n * reason appears under the field once the person leaves it or tries to send.\n * On a narrow card the role and Invite wrap below the field together instead\n * of being pushed past the card's edge.\n */\n\nimport { Button, Input } from '@tangle-network/ui/primitives'\nimport { useId, useState } from 'react'\nimport type { WorkspaceRole } from '../../teams/roles'\nimport { INVITATION_EMAIL_HINT, isInvitationEmail } from '../../teams/invitations'\nimport { RoleSelect } from './RoleSelect'\n\ninterface InviteFormProps {\n label: string\n placeholder: string\n /** Send the invite; resolve true to clear the field, false to keep it for a retry. */\n onInvite(input: { email: string; role: WorkspaceRole }): Promise<boolean>\n}\n\nexport function InviteForm({ label, placeholder, onInvite }: InviteFormProps) {\n const id = useId()\n const [email, setEmail] = useState('')\n const [role, setRole] = useState<WorkspaceRole>('editor')\n const [inviting, setInviting] = useState(false)\n // Reward early, flag late: the hint waits until the person leaves the field\n // or tries to send, then follows every keystroke until the address is valid.\n const [checked, setChecked] = useState(false)\n\n const valid = isInvitationEmail(email)\n const showHint = checked && email.trim() !== '' && !valid\n\n async function submit() {\n if (inviting) return\n if (!valid) {\n setChecked(true)\n return\n }\n setInviting(true)\n try {\n if (await onInvite({ email: email.trim(), role })) {\n setEmail('')\n setChecked(false)\n }\n } finally {\n setInviting(false)\n }\n }\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor={`${id}-email`} className=\"text-sm font-medium text-[var(--text-secondary)]\">{label}</label>\n <div className=\"flex flex-wrap gap-2\">\n <Input\n id={`${id}-email`}\n type=\"email\"\n inputMode=\"email\"\n autoComplete=\"off\"\n placeholder={placeholder}\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 onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void submit()\n }\n }}\n className=\"min-w-[12rem] flex-1\"\n />\n <div className=\"ml-auto flex shrink-0 gap-2\">\n <RoleSelect value={role} ariaLabel=\"Invite role\" onChange={setRole} />\n <Button type=\"button\" onClick={() => void submit()} disabled={inviting || !valid}>\n {inviting ? 'Inviting…' : 'Invite'}\n </Button>\n </div>\n </div>\n {showHint && (\n <p id={`${id}-hint`} role=\"alert\" className=\"text-sm text-[var(--surface-danger-text)]\">\n {INVITATION_EMAIL_HINT}\n </p>\n )}\n </div>\n )\n}\n","/**\n * Shared role dropdown for the teams panels — the popover-listbox pattern the\n * design system requires in place of a native `<select>` (same contract as the\n * design-canvas Toolbar's SelectControl: bordered trigger, chevron glyph, and a\n * `role=\"listbox\"` panel on the L3 popover surface with click-outside /\n * Escape-to-close). Both panels assign from the same workspace-role set, so the\n * options live here too.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { WorkspaceRole } from '../../teams/roles'\n\n// Each role says what it adds, so choosing one never needs a help page.\nconst ASSIGNABLE_ROLES: ReadonlyArray<{ value: WorkspaceRole; label: string; description: string }> = [\n { value: 'viewer', label: 'Viewer', description: 'Can read everything' },\n { value: 'editor', label: 'Editor', description: 'Can also chat and edit' },\n { value: 'admin', label: 'Admin', description: 'Can also manage settings and people' },\n]\n\nfunction ChevronDownGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\nexport interface RoleSelectProps {\n value: WorkspaceRole\n onChange(role: WorkspaceRole): void\n /** Accessible name (no visible label — the panels label the row). */\n ariaLabel: string\n disabled?: boolean\n}\n\nexport function RoleSelect({ value, onChange, ariaLabel, disabled }: RoleSelectProps) {\n const [open, setOpen] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const current = ASSIGNABLE_ROLES.find((option) => option.value === value)\n\n useEffect(() => {\n if (!open) return\n function onDocPointer(event: PointerEvent) {\n if (ref.current && !ref.current.contains(event.target as Node)) setOpen(false)\n }\n function onKey(event: KeyboardEvent) {\n if (event.key === 'Escape') setOpen(false)\n }\n document.addEventListener('pointerdown', onDocPointer)\n document.addEventListener('keydown', onKey)\n return () => {\n document.removeEventListener('pointerdown', onDocPointer)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n return (\n <div ref={ref} className=\"relative\">\n <button\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n onClick={() => setOpen((v) => !v)}\n className=\"flex items-center justify-between gap-1.5 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-1.5 text-sm text-[var(--text-secondary)] hover:border-[var(--brand-primary)] disabled:cursor-default disabled:opacity-40\"\n >\n <span>{current?.label ?? value}</span>\n <ChevronDownGlyph className=\"h-3 w-3 text-[var(--text-muted)]\" />\n </button>\n {open ? (\n <div\n role=\"listbox\"\n aria-label={ariaLabel}\n className=\"absolute right-0 top-full z-50 mt-1 flex w-64 flex-col rounded border border-[var(--card-edge)] bg-[hsl(var(--popover))] py-1 shadow-[var(--shadow-overlay)]\"\n >\n {ASSIGNABLE_ROLES.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={option.value === value}\n onClick={() => {\n onChange(option.value)\n setOpen(false)\n }}\n className={`flex flex-col px-3 py-2 text-left text-sm hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] ${\n option.value === value ? 'text-[var(--brand-primary)]' : 'text-[var(--text-primary)]'\n }`}\n >\n <span className=\"font-medium\">{option.label}</span>\n <span className=\"text-[var(--text-muted)]\">{option.description}</span>\n </button>\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n","/**\n * Workspace members panel: list members, invite by email + role, change a\n * member's role, remove a member. Fully callback-driven — the host supplies the\n * data and the async `onInvite`/`onChangeRole`/`onRemove` callbacks (backed by\n * `./teams/members-api`), so this imports no app router, fetch client, or toast.\n * Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n *\n * Role gating mirrors the API: only admins/owners see role selects and the\n * remove control; inherited org owners and explicit owners are not editable\n * here (org-level concern). The invite role select offers `admin` only to\n * admins/owners.\n */\n\nimport type { WorkspaceRole } from '../../teams/roles'\nimport { hasWorkspaceRole } from '../../teams/roles'\nimport type { MemberView, MembersPanelProps } from '../contracts'\nimport { InviteForm } from './InviteForm'\nimport { RoleSelect } from './RoleSelect'\n\nexport function MembersPanel({\n members,\n currentRole,\n onInvite,\n onChangeRole,\n onRemove,\n onNotice,\n showInviteForm = true,\n}: MembersPanelProps) {\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n async function invite(input: { email: string; role: WorkspaceRole }): Promise<boolean> {\n try {\n const result = await onInvite(input)\n notify('success', result && 'inviteUrl' in result && result.inviteUrl ? `Invite link ready for ${input.email}` : `Invited ${input.email}`)\n return true\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to invite')\n return false\n }\n }\n\n async function changeRole(memberId: string, role: WorkspaceRole) {\n try {\n await onChangeRole({ memberId, role })\n notify('success', 'Role updated')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to update role')\n }\n }\n\n async function remove(memberId: string) {\n try {\n await onRemove({ memberId })\n notify('success', 'Member removed')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to remove member')\n }\n }\n\n return (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"text-sm font-semibold text-[var(--text-primary)]\">Members</h4>\n <span className=\"text-sm text-[var(--text-muted)]\">{members.length} total</span>\n </div>\n <div className=\"flex flex-col gap-2\">\n {members.map((member) => (\n <MemberRow\n key={member.id}\n member={member}\n canManage={canManage}\n onChangeRole={changeRole}\n onRemove={remove}\n />\n ))}\n {members.length === 0 && (\n <p className=\"text-sm text-[var(--text-muted)]\">No team members yet.</p>\n )}\n </div>\n\n {canManage && showInviteForm && (\n <div className=\"border-t border-[var(--border-default)] pt-3\">\n <InviteForm label=\"Invite member\" placeholder=\"Email address\" onInvite={invite} />\n </div>\n )}\n </section>\n )\n}\n\ninterface MemberRowProps {\n member: MemberView\n canManage: boolean\n onChangeRole(memberId: string, role: WorkspaceRole): void\n onRemove(memberId: string): void\n}\n\nfunction MemberRow({ member, canManage, onChangeRole, onRemove }: MemberRowProps) {\n const pending = member.acceptedAt == null\n const label = member.name ?? member.email ?? 'Unknown'\n const initial = (member.name?.[0] ?? member.email?.[0] ?? '?').toUpperCase()\n const isOwner = member.role === 'owner'\n const editable = canManage && !isOwner && !member.inherited\n\n return (\n <div className=\"flex flex-wrap items-center justify-between gap-x-3 gap-y-2 border-b border-[var(--border-default)] py-2 last:border-0\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-input)] text-sm font-semibold text-[var(--text-secondary)]\">\n {initial}\n </div>\n <div className=\"min-w-0\">\n <p className=\"flex flex-wrap items-center gap-x-2 text-sm font-medium text-[var(--text-primary)]\">\n <span className=\"min-w-0 [overflow-wrap:anywhere]\">{label}</span>\n {pending && (\n <span className=\"rounded border border-[var(--border-default)] px-1.5 py-0.5 text-sm font-medium text-[var(--text-muted)]\">\n Pending\n </span>\n )}\n </p>\n {member.name && member.email && (\n <p className=\"text-sm text-[var(--text-muted)] [overflow-wrap:anywhere]\">{member.email}</p>\n )}\n </div>\n </div>\n\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n {isOwner ? (\n <span className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-sm text-[var(--text-secondary)]\">\n {member.inherited ? 'Org Admin' : 'Owner'}\n </span>\n ) : editable ? (\n <>\n <RoleSelect\n value={member.role}\n ariaLabel={`Role for ${label}`}\n onChange={(role) => onChangeRole(member.id, role)}\n />\n <button\n type=\"button\"\n aria-label={`Remove ${label}`}\n onClick={() => onRemove(member.id)}\n className=\"rounded px-2 py-1 text-sm text-[var(--text-danger)] hover:bg-[var(--border-default)]\"\n >\n Remove\n </button>\n </>\n ) : (\n <span className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-sm capitalize text-[var(--text-secondary)]\">\n {member.role}\n </span>\n )}\n </div>\n </div>\n )\n}\n","/**\n * Email-invitation panel: invite by email + role, and a history of every\n * invitation (pending / accepted / expired / revoked) with its email-delivery\n * status. Pending rows expose copy-link / resend / revoke. Fully callback-driven\n * — the host supplies the data and the async `onInvite` / `onResend` / `onRevoke`\n * callbacks (backed by `./teams/invitations-api`), so this imports no app router,\n * fetch client, or toast. Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n *\n * Mount alongside a list-only `MembersPanel` (`showInviteForm={false}`): the\n * members panel shows accepted members, this owns the invite flow + pending list.\n */\n\nimport { useState } from 'react'\nimport { hasWorkspaceRole, type WorkspaceRole } from '../../teams/roles'\nimport type { InvitationView, InvitationsPanelProps } from '../contracts'\nimport { InviteForm } from './InviteForm'\n\nconst BADGE_BASE = 'inline-flex items-center rounded border px-2 py-0.5 text-sm font-medium'\n\n// One badge per invitation, for where it stands: green = joined, amber =\n// waiting on the invitee, red = withdrawn, muted = lapsed. Email delivery is a\n// detail of a pending invitation, so it is said in words on the line below,\n// never as a second badge beside \"Joined\".\nconst STATUS: Record<InvitationView['status'], { label: string; tone: string }> = {\n accepted: { label: 'Joined', tone: 'border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]' },\n pending: { label: 'Pending', tone: 'border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]' },\n revoked: { label: 'Revoked', tone: 'border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]' },\n expired: { label: 'Expired', tone: 'border-[var(--border-default)] text-[var(--text-muted)]' },\n}\n\n/** Uppercase the role's first letter in JS (not CSS `capitalize`, which would\n * also hit the lowercase words that follow it on the same line). */\nfunction capitalize(text: string): string {\n return text.length === 0 ? text : text[0]!.toUpperCase() + text.slice(1)\n}\n\nfunction shortDate(value: Date | number | string): string {\n return new Date(value).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\n/** The line under the address: the role, then only what is true of this invitation now. */\nfunction invitationDetail(invitation: InvitationView): string {\n const role = capitalize(invitation.permissions)\n switch (invitation.status) {\n case 'accepted':\n return invitation.acceptedAt ? `${role} · joined ${shortDate(invitation.acceptedAt)}` : role\n case 'pending': {\n const delivery = invitation.emailStatus === 'sent' ? 'email sent' : invitation.emailStatus === 'not_sent' ? 'email not sent yet' : null\n return [role, delivery, `expires ${shortDate(invitation.expiresAt)}`].filter(Boolean).join(' · ')\n }\n case 'expired':\n return `${role} · expired ${shortDate(invitation.expiresAt)}`\n case 'revoked':\n return role\n }\n}\n\nexport function InvitationsPanel({\n invitations,\n currentRole,\n onInvite,\n onResend,\n onRevoke,\n onCopy,\n onNotice,\n}: InvitationsPanelProps) {\n const [busyId, setBusyId] = useState<string | null>(null)\n\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n async function invite(input: { email: string; role: WorkspaceRole }): Promise<boolean> {\n try {\n await onInvite(input)\n notify('success', `Invitation sent to ${input.email}`)\n return true\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to invite')\n return false\n }\n }\n\n async function copyLink(inviteUrl: string) {\n try {\n if (onCopy) await onCopy({ inviteUrl })\n else await navigator.clipboard.writeText(inviteUrl)\n notify('success', 'Invite link copied')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to copy link')\n }\n }\n\n async function resend(invitationId: string) {\n setBusyId(invitationId)\n try {\n await onResend({ invitationId })\n notify('success', 'Invitation resent')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to resend invitation')\n } finally {\n setBusyId(null)\n }\n }\n\n async function revoke(invitationId: string) {\n setBusyId(invitationId)\n try {\n await onRevoke({ invitationId })\n notify('success', 'Invitation revoked')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to revoke invitation')\n } finally {\n setBusyId(null)\n }\n }\n\n return (\n <section className=\"flex flex-col gap-4\">\n {canManage && <InviteForm label=\"Invite by email\" placeholder=\"colleague@example.com\" onInvite={invite} />}\n\n <div className=\"flex flex-col gap-2 border-t border-[var(--border-default)] pt-3\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"text-sm font-semibold text-[var(--text-primary)]\">Invitation history</h4>\n <span className=\"text-sm text-[var(--text-muted)]\">{invitations.length} total</span>\n </div>\n {invitations.length === 0 ? (\n <p className=\"text-sm text-[var(--text-muted)]\">No invitations yet.</p>\n ) : (\n invitations.map((invitation) => (\n <InvitationRow\n key={invitation.id}\n invitation={invitation}\n canManage={canManage}\n busy={busyId === invitation.id}\n onCopy={() => void copyLink(invitation.inviteUrl)}\n onResend={() => void resend(invitation.id)}\n onRevoke={() => void revoke(invitation.id)}\n />\n ))\n )}\n </div>\n </section>\n )\n}\n\ninterface InvitationRowProps {\n invitation: InvitationView\n canManage: boolean\n busy: boolean\n onCopy(): void\n onResend(): void\n onRevoke(): void\n}\n\nfunction InvitationRow({ invitation, canManage, busy, onCopy, onResend, onRevoke }: InvitationRowProps) {\n const isPending = invitation.status === 'pending'\n const status = STATUS[invitation.status]\n\n return (\n <div className=\"rounded-lg border border-[var(--border-default)] bg-[var(--bg-input)] p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-[var(--text-primary)]\" title={invitation.email}>{invitation.email}</p>\n <p className=\"text-sm text-[var(--text-muted)]\">{invitationDetail(invitation)}</p>\n {isPending && invitation.emailStatus === 'failed' && (\n <p className=\"mt-1 text-sm text-[var(--surface-danger-text)]\">The email was not delivered. Copy the link and send it yourself.</p>\n )}\n </div>\n <span className={`${BADGE_BASE} ${status.tone} shrink-0`}>{status.label}</span>\n </div>\n {canManage && isPending && (\n <div className=\"mt-3 flex flex-wrap items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCopy}\n disabled={busy}\n className=\"rounded border border-[var(--border-default)] px-2.5 py-1 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] disabled:opacity-50\"\n >\n Copy link\n </button>\n <button\n type=\"button\"\n onClick={onResend}\n disabled={busy}\n className=\"rounded border border-[var(--border-default)] px-2.5 py-1 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] disabled:opacity-50\"\n >\n {busy ? 'Working…' : 'Resend'}\n </button>\n <button\n type=\"button\"\n onClick={onRevoke}\n disabled={busy}\n className=\"rounded px-2.5 py-1 text-sm text-[var(--text-danger)] hover:bg-[var(--border-default)] disabled:opacity-50\"\n >\n Revoke\n </button>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Invite-accept surface for `/invite/:token`. Renders the invite state (valid /\n * invalid / already-accepted), handles the signed-out path (sign in or create\n * an account with the invited email), the email-mismatch path (switch account),\n * and the accept action. Callback-driven: the host supplies the resolved\n * `details` and `onAccept` / `onNavigate`, so this imports no app router or\n * fetch client. Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n */\n\nimport { useState } from 'react'\nimport type { InviteAcceptPageProps } from '../contracts'\nimport { BrandMark } from './BrandMark'\n\nexport function InviteAcceptPage({ details, onAccept, onNavigate, onResendVerification }: InviteAcceptPageProps) {\n const [accepting, setAccepting] = useState(false)\n const [acceptError, setAcceptError] = useState<string | null>(null)\n const [accepted, setAccepted] = useState(false)\n const [resendingVerification, setResendingVerification] = useState(false)\n const [verificationSent, setVerificationSent] = useState(false)\n\n if (details.status === 'invalid') {\n return (\n <Shell title=\"Invalid invitation\" body=\"This link is invalid or already used. Ask the workspace admin to send a new one.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Go to sign in</PrimaryButton>\n </Shell>\n )\n }\n\n if (details.status === 'already-accepted') {\n return (\n <Shell title=\"Already accepted\" body=\"This invitation has already been accepted.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'open-app' })}>Open workspace</PrimaryButton>\n </Shell>\n )\n }\n\n if (details.status === 'expired') {\n return (\n <Shell title=\"Invitation expired\" body=\"This invitation has expired. Ask the workspace admin to send a new one.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Go to sign in</PrimaryButton>\n </Shell>\n )\n }\n\n if (details.status === 'revoked') {\n return (\n <Shell title=\"Invitation revoked\" body=\"This invitation has been revoked. Ask the workspace admin to send a new one.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Go to sign in</PrimaryButton>\n </Shell>\n )\n }\n\n const workspaceName = details.workspaceName ?? 'a workspace'\n const inviterPrefix = details.inviterName ? `${details.inviterName} invited you` : \"You've been invited\"\n const roleSuffix = details.role ? ` as ${details.role}` : ''\n\n if (!details.currentUserEmail) {\n return (\n <Shell title=\"You've been invited\">\n <p className=\"mb-4 text-sm text-[var(--text-secondary)]\">\n {inviterPrefix} to join <span className=\"font-medium text-[var(--text-primary)]\">{workspaceName}</span>{roleSuffix}.\n </p>\n {details.inviteEmail && (\n <p className=\"mb-6 text-sm text-[var(--text-secondary)]\">\n Sign in or create an account with{' '}\n <span className=\"font-medium text-[var(--text-primary)]\">{details.inviteEmail}</span> to accept.\n </p>\n )}\n <div className=\"flex gap-2\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Sign in</PrimaryButton>\n <SecondaryButton onClick={() => onNavigate({ kind: 'sign-up' })}>Create account</SecondaryButton>\n </div>\n </Shell>\n )\n }\n\n if (accepted) {\n return (\n <Shell title=\"Welcome!\">\n <p className=\"text-sm text-[var(--text-secondary)]\">\n You've joined <span className=\"font-medium text-[var(--text-primary)]\">{workspaceName}</span>.\n </p>\n </Shell>\n )\n }\n\n const emailMismatch = Boolean(\n details.inviteEmail &&\n details.inviteEmail.toLowerCase() !== details.currentUserEmail.toLowerCase(),\n )\n const needsVerification = Boolean(details.needsEmailVerification) && !emailMismatch\n const expiryLabel = details.expiresAt != null\n ? new Date(details.expiresAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n : null\n\n async function handleAccept() {\n setAccepting(true)\n setAcceptError(null)\n try {\n const result = await onAccept()\n setAccepted(true)\n if (result && 'workspaceId' in result && result.workspaceId) {\n onNavigate({ kind: 'open-app', workspaceId: result.workspaceId })\n }\n } catch (err) {\n setAcceptError(err instanceof Error ? err.message : 'Failed to accept invitation')\n } finally {\n setAccepting(false)\n }\n }\n\n async function handleResendVerification() {\n if (!onResendVerification || resendingVerification) return\n setResendingVerification(true)\n try {\n await onResendVerification()\n setVerificationSent(true)\n } finally {\n setResendingVerification(false)\n }\n }\n\n return (\n <Shell title=\"Join workspace\">\n <p className=\"mb-4 text-sm text-[var(--text-secondary)]\">\n {inviterPrefix} to join <span className=\"font-medium text-[var(--text-primary)]\">{workspaceName}</span>{roleSuffix}.\n </p>\n <p className=\"mb-4 text-sm text-[var(--text-secondary)]\">\n Signed in as <span className=\"font-medium text-[var(--text-primary)]\">{details.currentUserEmail}</span>\n </p>\n {expiryLabel && (\n <p className=\"mb-4 text-xs text-[var(--text-muted)]\">This invitation expires on {expiryLabel}.</p>\n )}\n {emailMismatch && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-[var(--border-default)] px-4 py-2 text-sm text-[var(--text-warning)]\"\n >\n This invitation was sent to <span className=\"font-medium\">{details.inviteEmail}</span>. Switch to that account to accept it.\n </div>\n )}\n {needsVerification && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-[var(--border-default)] px-4 py-2 text-sm text-[var(--text-warning)]\"\n >\n {verificationSent\n ? 'Verification email sent — check your inbox, then refresh this page to accept.'\n : 'Verify your email address before you can accept this invitation.'}\n </div>\n )}\n {acceptError && (\n <p role=\"alert\" className=\"mb-4 text-sm text-[var(--text-danger)]\">{acceptError}</p>\n )}\n <div className=\"flex gap-2\">\n {emailMismatch ? (\n <PrimaryButton onClick={() => onNavigate({ kind: 'switch-account' })}>Switch account</PrimaryButton>\n ) : needsVerification ? (\n onResendVerification ? (\n <PrimaryButton\n onClick={() => void handleResendVerification()}\n disabled={resendingVerification || verificationSent}\n >\n {verificationSent ? 'Verification sent' : resendingVerification ? 'Sending…' : 'Resend verification email'}\n </PrimaryButton>\n ) : (\n <PrimaryButton onClick={() => onNavigate({ kind: 'open-app' })}>Open app</PrimaryButton>\n )\n ) : (\n <PrimaryButton onClick={() => void handleAccept()} disabled={accepting}>\n {accepting ? 'Accepting…' : 'Accept invitation'}\n </PrimaryButton>\n )}\n <SecondaryButton onClick={() => onNavigate({ kind: 'open-app' })}>Not now</SecondaryButton>\n </div>\n </Shell>\n )\n}\n\nfunction Shell({ title, body, children }: { title: string; body?: string; children: React.ReactNode }) {\n return (\n <div className=\"mx-auto flex w-full max-w-sm flex-col\">\n <span className=\"mb-4 flex items-center gap-2 text-[var(--text-muted)]\">\n <BrandMark size={22} className=\"shrink-0\" />\n <span className=\"text-xs font-semibold uppercase tracking-[0.05em]\">Tangle Teams</span>\n </span>\n <h1 className=\"mb-1 text-xl font-semibold tracking-tight text-[var(--text-primary)]\">{title}</h1>\n {body && <p className=\"mb-6 text-sm text-[var(--text-secondary)]\">{body}</p>}\n {children}\n </div>\n )\n}\n\nfunction PrimaryButton({ children, onClick, disabled }: { children: React.ReactNode; onClick(): void; disabled?: boolean }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className=\"flex-1 rounded bg-[var(--brand-primary)] px-4 py-2 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {children}\n </button>\n )\n}\n\nfunction SecondaryButton({ children, onClick }: { children: React.ReactNode; onClick(): void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"flex-1 rounded border border-[var(--border-default)] px-4 py-2 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n {children}\n </button>\n )\n}\n","/**\n * Load the canonical Tangle knot only when a teams surface renders it.\n * `/brand` imports the optional `@tangle-network/brand` peer, so a failed load\n * renders a fixed-size spacer instead of crashing the panel.\n */\n\nimport { lazy, Suspense } from 'react'\nimport type { ComponentType } from 'react'\n\nexport interface BrandMarkProps {\n size: number\n className?: string\n}\n\n/** Preserve the mark's footprint while its optional package loads or is absent. */\nfunction MarkSpacer({ size }: { size: number }) {\n return <span aria-hidden style={{ display: 'inline-block', width: size, height: size }} />\n}\n\nconst LazyKnot = lazy(async () => {\n try {\n const mod = await import('../../brand')\n return { default: mod.TangleKnot as ComponentType<BrandMarkProps> }\n } catch {\n return { default: MarkSpacer as ComponentType<BrandMarkProps> }\n }\n})\n\nexport function BrandMark({ size, className }: BrandMarkProps) {\n return (\n <Suspense fallback={<MarkSpacer size={size} />}>\n <LazyKnot size={size} className={className} />\n </Suspense>\n )\n}\n"],"mappings":"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","names":["useState","jsx","jsxs","useState","jsx","jsxs","useState","jsx","jsxs","useState","useState","jsx","jsx","jsxs","useState"]}
1
+ {"version":3,"sources":["../../src/teams-react/components/MembersPanel.tsx","../../src/teams-react/components/InviteForm.tsx","../../src/teams-react/components/InvitationsPanel.tsx","../../src/teams-react/components/InviteAcceptPage.tsx","../../src/teams-react/components/BrandMark.tsx"],"sourcesContent":["/**\n * Workspace members panel: list members, invite by email + role, change a\n * member's role, remove a member. Fully callback-driven — the host supplies the\n * data and the async `onInvite`/`onChangeRole`/`onRemove` callbacks (backed by\n * `./teams/members-api`), so this imports no app router, fetch client, or toast.\n * Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n *\n * Role gating mirrors the API and `MembersSection` (`canManageMember`): only\n * admins/owners see role selects and the remove control; inherited org owners,\n * explicit owners and the viewer's own row are not editable here. The invite\n * role select offers `admin` only to admins/owners.\n *\n * Below 36rem of panel a member's role picker takes its own full-width line\n * under the name, and Remove stays beside the name, so the longest capability\n * name fits and nothing passes the card's edge.\n */\n\nimport { Button, Heading } from '@tangle-network/ui/primitives'\nimport type { WorkspaceRole } from '../../teams/roles'\nimport { hasWorkspaceRole } from '../../teams/roles'\nimport type { MemberView, MembersPanelProps } from '../contracts'\nimport { InviteForm } from './InviteForm'\nimport { canManageMember, isCurrentMember, memberRoleLabel } from './member-access'\nimport { RoleSelect } from './RoleSelect'\n\nexport function MembersPanel({\n members,\n currentRole,\n currentUserId,\n onInvite,\n onChangeRole,\n onRemove,\n onNotice,\n showInviteForm = true,\n}: MembersPanelProps) {\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n async function invite(input: { email: string; role: WorkspaceRole }): Promise<boolean> {\n try {\n const result = await onInvite(input)\n notify('success', result && 'inviteUrl' in result && result.inviteUrl ? `Invite link ready for ${input.email}` : `Invited ${input.email}`)\n return true\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to invite')\n return false\n }\n }\n\n async function changeRole(memberId: string, role: WorkspaceRole) {\n try {\n await onChangeRole({ memberId, role })\n notify('success', 'Role updated')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to update role')\n }\n }\n\n async function remove(memberId: string) {\n try {\n await onRemove({ memberId })\n notify('success', 'Member removed')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to remove member')\n }\n }\n\n return (\n <section className=\"@container/members flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <Heading as=\"h4\" variant=\"subsection\">Members</Heading>\n <span className=\"text-sm text-[var(--text-muted)]\">{members.length} total</span>\n </div>\n <div className=\"flex flex-col gap-2\">\n {members.map((member) => (\n <MemberRow\n key={member.id}\n member={member}\n editable={canManageMember(member, currentRole, currentUserId)}\n self={isCurrentMember(member, currentUserId)}\n onChangeRole={changeRole}\n onRemove={remove}\n />\n ))}\n {members.length === 0 && (\n <p className=\"text-sm text-[var(--text-muted)]\">No team members yet.</p>\n )}\n </div>\n\n {canManage && showInviteForm && (\n <div className=\"border-t border-[var(--border-default)] pt-3\">\n <InviteForm label=\"Invite member\" placeholder=\"Email address\" onInvite={invite} />\n </div>\n )}\n </section>\n )\n}\n\ninterface MemberRowProps {\n member: MemberView\n editable: boolean\n self: boolean\n onChangeRole(memberId: string, role: WorkspaceRole): void\n onRemove(memberId: string): void\n}\n\nfunction MemberRow({ member, editable, self, onChangeRole, onRemove }: MemberRowProps) {\n const pending = member.acceptedAt == null\n const label = member.name ?? member.email ?? 'Unknown'\n const initial = (member.name?.[0] ?? member.email?.[0] ?? '?').toUpperCase()\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-2 border-b border-[var(--border-default)] py-2 last:border-0\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3 @max-[36rem]/members:basis-40\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-input)] text-sm font-semibold text-[var(--text-secondary)]\">\n {initial}\n </div>\n <div className=\"min-w-0\">\n <p className=\"flex flex-wrap items-center gap-x-2 text-sm font-medium text-[var(--text-primary)]\">\n <span className=\"min-w-0 [overflow-wrap:anywhere]\">{label}{self && <span className=\"font-normal text-[var(--text-muted)]\"> (you)</span>}</span>\n {pending && (\n <span className=\"rounded border border-[var(--border-default)] px-1.5 py-0.5 text-sm font-medium text-[var(--text-muted)]\">\n Pending\n </span>\n )}\n </p>\n {member.name && member.email && (\n <p className=\"text-sm text-[var(--text-muted)] [overflow-wrap:anywhere]\">{member.email}</p>\n )}\n </div>\n </div>\n\n {editable ? (\n <>\n <div className=\"w-72 shrink-0 @max-[36rem]/members:order-last @max-[36rem]/members:w-full\">\n <RoleSelect\n value={member.role}\n ariaLabel={`Role for ${label}`}\n onChange={(role) => onChangeRole(member.id, role)}\n />\n </div>\n <Button type=\"button\" variant=\"destructive-outline\" aria-label={`Remove ${label}`} onClick={() => onRemove(member.id)}>\n Remove\n </Button>\n </>\n ) : (\n <span className=\"ml-auto rounded border border-[var(--border-default)] px-2 py-0.5 text-sm text-[var(--text-secondary)]\">\n {memberRoleLabel(member)}\n </span>\n )}\n </div>\n )\n}\n","/**\n * The invite-by-email row both team panels share: address, role, Invite. The\n * address is checked with the same rule the invitations API enforces, so\n * Invite stays disabled until the address could be delivered to, and the\n * reason appears under the field once the person leaves it or tries to send.\n * On a narrow card the field, the role and Invite stack, the role at full\n * width so its longest name fits, and nothing passes the card's edge.\n */\n\nimport { Button, Input } from '@tangle-network/ui/primitives'\nimport { useId, useState } from 'react'\nimport type { WorkspaceRole } from '../../teams/roles'\nimport { INVITATION_EMAIL_HINT, isInvitationEmail } from '../../teams/invitations'\nimport { RoleSelect } from './RoleSelect'\n\ninterface InviteFormProps {\n label: string\n placeholder: string\n /** Send the invite; resolve true to clear the field, false to keep it for a retry. */\n onInvite(input: { email: string; role: WorkspaceRole }): Promise<boolean>\n}\n\nexport function InviteForm({ label, placeholder, onInvite }: InviteFormProps) {\n const id = useId()\n const [email, setEmail] = useState('')\n const [role, setRole] = useState<WorkspaceRole>('editor')\n const [inviting, setInviting] = useState(false)\n // Reward early, flag late: the hint waits until the person leaves the field\n // or tries to send, then follows every keystroke until the address is valid.\n const [checked, setChecked] = useState(false)\n\n const valid = isInvitationEmail(email)\n const showHint = checked && email.trim() !== '' && !valid\n\n async function submit() {\n if (inviting) return\n if (!valid) {\n setChecked(true)\n return\n }\n setInviting(true)\n try {\n if (await onInvite({ email: email.trim(), role })) {\n setEmail('')\n setChecked(false)\n }\n } finally {\n setInviting(false)\n }\n }\n\n return (\n <div className=\"@container/invite flex flex-col gap-1.5\">\n <label htmlFor={`${id}-email`} className=\"text-sm font-medium text-[var(--text-secondary)]\">{label}</label>\n <div className=\"flex flex-wrap gap-2\">\n <Input\n id={`${id}-email`}\n type=\"email\"\n inputMode=\"email\"\n autoComplete=\"off\"\n placeholder={placeholder}\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 onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void submit()\n }\n }}\n className=\"min-w-[12rem] flex-1\"\n />\n <div className=\"w-72 shrink-0 @max-[36rem]/invite:w-full\">\n <RoleSelect value={role} ariaLabel=\"Invite role\" onChange={setRole} />\n </div>\n <Button type=\"button\" className=\"ml-auto\" onClick={() => void submit()} disabled={inviting || !valid}>\n {inviting ? 'Inviting…' : 'Invite'}\n </Button>\n </div>\n {showHint && (\n <p id={`${id}-hint`} role=\"alert\" className=\"text-sm text-[var(--surface-danger-text)]\">\n {INVITATION_EMAIL_HINT}\n </p>\n )}\n </div>\n )\n}\n","/**\n * Email-invitation panel: invite by email + role, and a history of every\n * invitation (pending / accepted / expired / revoked) with its email-delivery\n * status. Pending rows expose copy-link / resend / revoke. Fully callback-driven\n * — the host supplies the data and the async `onInvite` / `onResend` / `onRevoke`\n * callbacks (backed by `./teams/invitations-api`), so this imports no app router,\n * fetch client, or toast. Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n *\n * Mount alongside a list-only `MembersPanel` (`showInviteForm={false}`): the\n * members panel shows accepted members, this owns the invite flow + pending list.\n */\n\nimport { Button, Heading } from '@tangle-network/ui/primitives'\nimport { useState } from 'react'\nimport { hasWorkspaceRole, type WorkspaceRole } from '../../teams/roles'\nimport type { InvitationView, InvitationsPanelProps } from '../contracts'\nimport { invitationDetail } from './invitation-detail'\nimport { InviteForm } from './InviteForm'\n\nconst BADGE_BASE = 'inline-flex items-center rounded border px-2 py-0.5 text-sm font-medium'\n\n// One badge per invitation, for where it stands: green = joined, amber =\n// waiting on the invitee, red = withdrawn, muted = lapsed. Email delivery is a\n// detail of a pending invitation, so it is said in words on the line below,\n// never as a second badge beside \"Joined\".\nconst STATUS: Record<InvitationView['status'], { label: string; tone: string }> = {\n accepted: { label: 'Joined', tone: 'border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]' },\n pending: { label: 'Pending', tone: 'border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]' },\n revoked: { label: 'Revoked', tone: 'border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]' },\n expired: { label: 'Expired', tone: 'border-[var(--border-default)] text-[var(--text-muted)]' },\n}\n\nexport function InvitationsPanel({\n invitations,\n currentRole,\n onInvite,\n onResend,\n onRevoke,\n onCopy,\n onNotice,\n}: InvitationsPanelProps) {\n const [busyId, setBusyId] = useState<string | null>(null)\n\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n async function invite(input: { email: string; role: WorkspaceRole }): Promise<boolean> {\n try {\n await onInvite(input)\n notify('success', `Invitation sent to ${input.email}`)\n return true\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to invite')\n return false\n }\n }\n\n async function copyLink(inviteUrl: string) {\n try {\n if (onCopy) await onCopy({ inviteUrl })\n else await navigator.clipboard.writeText(inviteUrl)\n notify('success', 'Invite link copied')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to copy link')\n }\n }\n\n async function resend(invitationId: string) {\n setBusyId(invitationId)\n try {\n await onResend({ invitationId })\n notify('success', 'Invitation resent')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to resend invitation')\n } finally {\n setBusyId(null)\n }\n }\n\n async function revoke(invitationId: string) {\n setBusyId(invitationId)\n try {\n await onRevoke({ invitationId })\n notify('success', 'Invitation revoked')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to revoke invitation')\n } finally {\n setBusyId(null)\n }\n }\n\n return (\n <section className=\"flex flex-col gap-4\">\n {canManage && <InviteForm label=\"Invite by email\" placeholder=\"colleague@example.com\" onInvite={invite} />}\n\n <div className=\"flex flex-col gap-2 border-t border-[var(--border-default)] pt-3\">\n <div className=\"flex items-center justify-between\">\n <Heading as=\"h4\" variant=\"subsection\">Invitation history</Heading>\n <span className=\"text-sm text-[var(--text-muted)]\">{invitations.length} total</span>\n </div>\n {invitations.length === 0 ? (\n <p className=\"text-sm text-[var(--text-muted)]\">No invitations yet.</p>\n ) : (\n invitations.map((invitation) => (\n <InvitationRow\n key={invitation.id}\n invitation={invitation}\n canManage={canManage}\n busy={busyId === invitation.id}\n onCopy={() => void copyLink(invitation.inviteUrl)}\n onResend={() => void resend(invitation.id)}\n onRevoke={() => void revoke(invitation.id)}\n />\n ))\n )}\n </div>\n </section>\n )\n}\n\ninterface InvitationRowProps {\n invitation: InvitationView\n canManage: boolean\n busy: boolean\n onCopy(): void\n onResend(): void\n onRevoke(): void\n}\n\nfunction InvitationRow({ invitation, canManage, busy, onCopy, onResend, onRevoke }: InvitationRowProps) {\n const isPending = invitation.status === 'pending'\n const status = STATUS[invitation.status]\n\n return (\n <div className=\"rounded-lg border border-[var(--border-default)] bg-[var(--bg-input)] p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-[var(--text-primary)]\" title={invitation.email}>{invitation.email}</p>\n <p className=\"text-sm text-[var(--text-muted)]\">{invitationDetail(invitation)}</p>\n {isPending && invitation.emailStatus === 'failed' && (\n <p className=\"mt-1 text-sm text-[var(--surface-danger-text)]\">The email was not delivered. Copy the link and send it yourself.</p>\n )}\n </div>\n <span className={`${BADGE_BASE} ${status.tone} shrink-0`}>{status.label}</span>\n </div>\n {canManage && isPending && (\n <div className=\"mt-3 flex flex-wrap items-center justify-end gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={onCopy} disabled={busy}>Copy link</Button>\n <Button type=\"button\" variant=\"outline\" onClick={onResend} disabled={busy}>{busy ? 'Working…' : 'Resend'}</Button>\n <Button type=\"button\" variant=\"destructive-outline\" onClick={onRevoke} disabled={busy}>Revoke</Button>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Invite-accept surface for `/invite/:token`. Renders the invite state (valid /\n * invalid / already-accepted), handles the signed-out path (sign in or create\n * an account with the invited email), the email-mismatch path (switch account),\n * and the accept action. Callback-driven: the host supplies the resolved\n * `details` and `onAccept` / `onNavigate`, so this imports no app router or\n * fetch client. Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n */\n\nimport { useState } from 'react'\nimport type { InviteAcceptPageProps } from '../contracts'\nimport { BrandMark } from './BrandMark'\n\nexport function InviteAcceptPage({ details, onAccept, onNavigate, onResendVerification }: InviteAcceptPageProps) {\n const [accepting, setAccepting] = useState(false)\n const [acceptError, setAcceptError] = useState<string | null>(null)\n const [accepted, setAccepted] = useState(false)\n const [resendingVerification, setResendingVerification] = useState(false)\n const [verificationSent, setVerificationSent] = useState(false)\n\n if (details.status === 'invalid') {\n return (\n <Shell title=\"Invalid invitation\" body=\"This link is invalid or already used. Ask the workspace admin to send a new one.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Go to sign in</PrimaryButton>\n </Shell>\n )\n }\n\n if (details.status === 'already-accepted') {\n return (\n <Shell title=\"Already accepted\" body=\"This invitation has already been accepted.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'open-app' })}>Open workspace</PrimaryButton>\n </Shell>\n )\n }\n\n if (details.status === 'expired') {\n return (\n <Shell title=\"Invitation expired\" body=\"This invitation has expired. Ask the workspace admin to send a new one.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Go to sign in</PrimaryButton>\n </Shell>\n )\n }\n\n if (details.status === 'revoked') {\n return (\n <Shell title=\"Invitation revoked\" body=\"This invitation has been revoked. Ask the workspace admin to send a new one.\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Go to sign in</PrimaryButton>\n </Shell>\n )\n }\n\n const workspaceName = details.workspaceName ?? 'a workspace'\n const inviterPrefix = details.inviterName ? `${details.inviterName} invited you` : \"You've been invited\"\n const roleSuffix = details.role ? ` as ${details.role}` : ''\n\n if (!details.currentUserEmail) {\n return (\n <Shell title=\"You've been invited\">\n <p className=\"mb-4 text-sm text-[var(--text-secondary)]\">\n {inviterPrefix} to join <span className=\"font-medium text-[var(--text-primary)]\">{workspaceName}</span>{roleSuffix}.\n </p>\n {details.inviteEmail && (\n <p className=\"mb-6 text-sm text-[var(--text-secondary)]\">\n Sign in or create an account with{' '}\n <span className=\"font-medium text-[var(--text-primary)]\">{details.inviteEmail}</span> to accept.\n </p>\n )}\n <div className=\"flex gap-2\">\n <PrimaryButton onClick={() => onNavigate({ kind: 'sign-in' })}>Sign in</PrimaryButton>\n <SecondaryButton onClick={() => onNavigate({ kind: 'sign-up' })}>Create account</SecondaryButton>\n </div>\n </Shell>\n )\n }\n\n if (accepted) {\n return (\n <Shell title=\"Welcome!\">\n <p className=\"text-sm text-[var(--text-secondary)]\">\n You've joined <span className=\"font-medium text-[var(--text-primary)]\">{workspaceName}</span>.\n </p>\n </Shell>\n )\n }\n\n const emailMismatch = Boolean(\n details.inviteEmail &&\n details.inviteEmail.toLowerCase() !== details.currentUserEmail.toLowerCase(),\n )\n const needsVerification = Boolean(details.needsEmailVerification) && !emailMismatch\n const expiryLabel = details.expiresAt != null\n ? new Date(details.expiresAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n : null\n\n async function handleAccept() {\n setAccepting(true)\n setAcceptError(null)\n try {\n const result = await onAccept()\n setAccepted(true)\n if (result && 'workspaceId' in result && result.workspaceId) {\n onNavigate({ kind: 'open-app', workspaceId: result.workspaceId })\n }\n } catch (err) {\n setAcceptError(err instanceof Error ? err.message : 'Failed to accept invitation')\n } finally {\n setAccepting(false)\n }\n }\n\n async function handleResendVerification() {\n if (!onResendVerification || resendingVerification) return\n setResendingVerification(true)\n try {\n await onResendVerification()\n setVerificationSent(true)\n } finally {\n setResendingVerification(false)\n }\n }\n\n return (\n <Shell title=\"Join workspace\">\n <p className=\"mb-4 text-sm text-[var(--text-secondary)]\">\n {inviterPrefix} to join <span className=\"font-medium text-[var(--text-primary)]\">{workspaceName}</span>{roleSuffix}.\n </p>\n <p className=\"mb-4 text-sm text-[var(--text-secondary)]\">\n Signed in as <span className=\"font-medium text-[var(--text-primary)]\">{details.currentUserEmail}</span>\n </p>\n {expiryLabel && (\n <p className=\"mb-4 text-xs text-[var(--text-muted)]\">This invitation expires on {expiryLabel}.</p>\n )}\n {emailMismatch && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-[var(--border-default)] px-4 py-2 text-sm text-[var(--text-warning)]\"\n >\n This invitation was sent to <span className=\"font-medium\">{details.inviteEmail}</span>. Switch to that account to accept it.\n </div>\n )}\n {needsVerification && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-[var(--border-default)] px-4 py-2 text-sm text-[var(--text-warning)]\"\n >\n {verificationSent\n ? 'Verification email sent — check your inbox, then refresh this page to accept.'\n : 'Verify your email address before you can accept this invitation.'}\n </div>\n )}\n {acceptError && (\n <p role=\"alert\" className=\"mb-4 text-sm text-[var(--text-danger)]\">{acceptError}</p>\n )}\n <div className=\"flex gap-2\">\n {emailMismatch ? (\n <PrimaryButton onClick={() => onNavigate({ kind: 'switch-account' })}>Switch account</PrimaryButton>\n ) : needsVerification ? (\n onResendVerification ? (\n <PrimaryButton\n onClick={() => void handleResendVerification()}\n disabled={resendingVerification || verificationSent}\n >\n {verificationSent ? 'Verification sent' : resendingVerification ? 'Sending…' : 'Resend verification email'}\n </PrimaryButton>\n ) : (\n <PrimaryButton onClick={() => onNavigate({ kind: 'open-app' })}>Open app</PrimaryButton>\n )\n ) : (\n <PrimaryButton onClick={() => void handleAccept()} disabled={accepting}>\n {accepting ? 'Accepting…' : 'Accept invitation'}\n </PrimaryButton>\n )}\n <SecondaryButton onClick={() => onNavigate({ kind: 'open-app' })}>Not now</SecondaryButton>\n </div>\n </Shell>\n )\n}\n\nfunction Shell({ title, body, children }: { title: string; body?: string; children: React.ReactNode }) {\n return (\n <div className=\"mx-auto flex w-full max-w-sm flex-col\">\n <span className=\"mb-4 flex items-center gap-2 text-[var(--text-muted)]\">\n <BrandMark size={22} className=\"shrink-0\" />\n <span className=\"text-xs font-semibold uppercase tracking-[0.05em]\">Tangle Teams</span>\n </span>\n <h1 className=\"mb-1 text-xl font-semibold tracking-tight text-[var(--text-primary)]\">{title}</h1>\n {body && <p className=\"mb-6 text-sm text-[var(--text-secondary)]\">{body}</p>}\n {children}\n </div>\n )\n}\n\nfunction PrimaryButton({ children, onClick, disabled }: { children: React.ReactNode; onClick(): void; disabled?: boolean }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className=\"flex-1 rounded bg-[var(--brand-primary)] px-4 py-2 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {children}\n </button>\n )\n}\n\nfunction SecondaryButton({ children, onClick }: { children: React.ReactNode; onClick(): void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"flex-1 rounded border border-[var(--border-default)] px-4 py-2 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n {children}\n </button>\n )\n}\n","/**\n * Load the canonical Tangle knot only when a teams surface renders it.\n * `/brand` imports the optional `@tangle-network/brand` peer, so a failed load\n * renders a fixed-size spacer instead of crashing the panel.\n */\n\nimport { lazy, Suspense } from 'react'\nimport type { ComponentType } from 'react'\n\nexport interface BrandMarkProps {\n size: number\n className?: string\n}\n\n/** Preserve the mark's footprint while its optional package loads or is absent. */\nfunction MarkSpacer({ size }: { size: number }) {\n return <span aria-hidden style={{ display: 'inline-block', width: size, height: size }} />\n}\n\nconst LazyKnot = lazy(async () => {\n try {\n const mod = await import('../../brand')\n return { default: mod.TangleKnot as ComponentType<BrandMarkProps> }\n } catch {\n return { default: MarkSpacer as ComponentType<BrandMarkProps> }\n }\n})\n\nexport function BrandMark({ size, className }: BrandMarkProps) {\n return (\n <Suspense fallback={<MarkSpacer size={size} />}>\n <LazyKnot size={size} className={className} />\n </Suspense>\n )\n}\n"],"mappings":"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","names":["Button","jsx","jsxs","Button","Button","Heading","useState","jsx","jsxs","useState","Heading","Button","useState","jsx","jsx","jsxs","useState"]}
@@ -1882,15 +1882,56 @@
1882
1882
  .tangle-reading-article { width: 100%; max-width: var(--reading-max-width); overflow-wrap: anywhere; }
1883
1883
  .tangle-reading-nav a, .tangle-reading-outline a { display: block; padding: 0.5rem 0; color: hsl(var(--muted-foreground)); text-decoration: none; }
1884
1884
  .tangle-reading-nav a[aria-current]:not([aria-current="false"]), .tangle-reading-outline a[aria-current]:not([aria-current="false"]) { color: var(--accent-text); font-weight: 600; }
1885
+ /* The 13rem track exists only beside a nav, so an article with no nav keeps the full column. */
1885
1886
  @media(min-width: 64rem) {
1886
- .tangle-reading-shell { grid-template-columns: 13rem minmax(0,1fr); }
1887
+ .tangle-reading-shell:has(> .tangle-reading-nav) { grid-template-columns: 13rem minmax(0,1fr); }
1887
1888
  .tangle-reading-outline { display: none; }
1888
1889
  .tangle-reading-nav { position: sticky; top: var(--shell-header-height); align-self: start; max-height: calc(100dvh - var(--shell-header-height)); overflow: auto; }
1889
1890
  }
1890
1891
  @media(min-width: 80rem) {
1891
- .tangle-reading-shell:has(.tangle-reading-outline) { grid-template-columns: 13rem minmax(0,1fr) 11rem; }
1892
+ .tangle-reading-shell:has(> .tangle-reading-outline) { grid-template-columns: minmax(0,1fr) 11rem; }
1893
+ .tangle-reading-shell:where(:has(> .tangle-reading-nav)):has(> .tangle-reading-outline) { grid-template-columns: 13rem minmax(0,1fr) 11rem; }
1892
1894
  .tangle-reading-outline { display: block; position: sticky; top: var(--shell-header-height); align-self: start; }
1893
1895
  }
1896
+ .tangle-reading-header { grid-column: 1 / -1; min-width: 0; }
1897
+ /* PageShell measure="settings". The frame is the query container, so the layout follows the content area's width, not the viewport's.
1898
+ Below 60rem the navigation is a sticky tab strip whose track is the divider; from 60rem it is a sticky 13rem column beside a 48rem article.
1899
+ Without a nav the page is the 48rem column alone. */
1900
+ .tangle-settings-frame { container: tangle-settings / inline-size; width: 100%; min-width: 0; }
1901
+ .tangle-reading-shell[data-measure="settings"] { --tangle-settings-gutter: 1.5rem; grid-template-columns: minmax(0,1fr); gap: 0; max-width: calc(13rem + 2.5rem + 48rem + 4rem); padding: 1.5rem var(--tangle-settings-gutter) 3rem; }
1902
+ /* Without a nav the shell is the column and its gutters, so the centred shell centres the column and its header. */
1903
+ .tangle-reading-shell[data-measure="settings"]:not(:has(> .tangle-reading-nav)) { max-width: calc(48rem + 2 * var(--tangle-settings-gutter)); }
1904
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-header { margin-bottom: 0.5rem; }
1905
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-header > * { margin-bottom: 0; }
1906
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-outline { display: none; }
1907
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-article { max-width: 48rem; display: flex; flex-direction: column; gap: 1.5rem; }
1908
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav { position: sticky; top: 0; z-index: 3; display: flex; align-items: flex-end; gap: 0.25rem; height: var(--shell-header-height); max-height: none; margin: 0 calc(-1 * var(--tangle-settings-gutter)) 1.25rem; padding: 0 var(--tangle-settings-gutter); overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scrollbar-width: none; background: hsl(var(--background)); box-shadow: inset 0 -1px 0 hsl(var(--border)); }
1909
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav::-webkit-scrollbar { display: none; }
1910
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav > :is(ul, ol) { display: flex; flex: none; align-items: flex-end; gap: 0.25rem; margin: 0; padding: 0; list-style: none; }
1911
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a { flex: none; display: flex; align-items: center; gap: 0.5rem; min-height: var(--control-height-lg, 2.75rem); padding: 0 0.75rem; color: hsl(var(--muted-foreground)); font-size: var(--font-size-label, 0.875rem); font-weight: 500; line-height: 1.25rem; white-space: nowrap; text-decoration: none; transition: color var(--motion-control), background-color var(--motion-control), box-shadow var(--motion-control); }
1912
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a > svg { display: none; }
1913
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a > [data-count] { color: var(--text-dim); font-weight: 400; font-variant-numeric: tabular-nums; }
1914
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a:hover { color: hsl(var(--foreground)); box-shadow: inset 0 -2px 0 hsl(var(--hsl-ring) / 0.4); }
1915
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a[aria-current="page"] { color: var(--accent-text); font-weight: 500; background: hsl(var(--hsl-ring) / 0.07); box-shadow: inset 0 -2px 0 var(--ix-sel-ring); }
1916
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a:focus-visible { outline: 1px solid var(--focus-border); outline-offset: -1px; box-shadow: inset 0 0 0 4px var(--focus-halo); }
1917
+ @container tangle-settings (max-width: 40rem) {
1918
+ .tangle-reading-shell[data-measure="settings"] { --tangle-settings-gutter: 1rem; padding: 1rem var(--tangle-settings-gutter) 2.5rem; }
1919
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav { margin-bottom: 1rem; }
1920
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-article { gap: 1rem; }
1921
+ }
1922
+ @container tangle-settings (min-width: 60rem) {
1923
+ .tangle-reading-shell[data-measure="settings"] { --tangle-settings-gutter: 2rem; grid-template-columns: minmax(0,48rem); column-gap: 2.5rem; align-items: start; padding: 2rem; }
1924
+ .tangle-reading-shell[data-measure="settings"]:has(> .tangle-reading-nav) { grid-template-columns: 13rem minmax(0,48rem); }
1925
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-header { margin-bottom: 1.75rem; }
1926
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav { top: 2rem; flex-direction: column; align-items: stretch; gap: 2px; height: auto; margin: 0; padding: 0; overflow: visible; background: none; box-shadow: none; }
1927
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav > :is(ul, ol) { flex-direction: column; align-items: stretch; gap: 2px; }
1928
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a { min-height: var(--control-height, 2.25rem); gap: 0.625rem; padding: 0 0.625rem; border-radius: var(--radius-md); }
1929
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a > svg { display: block; flex: none; width: 1rem; height: 1rem; }
1930
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a > [data-count] { margin-inline-start: auto; }
1931
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a:hover { background: var(--ix-hover-bg); box-shadow: none; }
1932
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a[aria-current="page"] { background: var(--ix-sel-bg); box-shadow: inset 0 0 0 1px var(--ix-sel-ring); }
1933
+ .tangle-reading-shell[data-measure="settings"] > .tangle-reading-nav a:focus-visible { outline-offset: 0; box-shadow: 0 0 0 4px var(--focus-halo); }
1934
+ }
1894
1935
  /* Native dialog has the same solid reading surface as React's Dialog/Sheet. */
1895
1936
  .tangle-native-dialog { max-width: min(32rem,calc(100% - 2rem)); max-height: calc(100dvh - 2rem); overflow: auto; margin: auto; padding: 1.5rem; border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg); background: hsl(var(--card)); color: hsl(var(--card-foreground)); }
1896
1937
  .tangle-native-dialog::backdrop { background: var(--scrim); }
@@ -9,7 +9,7 @@ import {
9
9
  restrictTaxonomy,
10
10
  verifyCapabilityToken,
11
11
  verifyExpiringCapabilityToken
12
- } from "../chunk-75ZIWV4U.js";
12
+ } from "../chunk-QAF7ZTO3.js";
13
13
  import {
14
14
  DEFAULT_APP_TOOL_PATHS,
15
15
  DEFAULT_HEADER_NAMES,
@@ -23,7 +23,7 @@ import {
23
23
  unresolvableSurfaceCredential
24
24
  } from "../chunk-6A7MYOUI.js";
25
25
  import "../chunk-S5SRJJQG.js";
26
- import "../chunk-YXLN4PXK.js";
26
+ import "../chunk-2RCPPOQ4.js";
27
27
  import {
28
28
  APP_TOOL_NAMES,
29
29
  buildAppToolOpenAITools,
@@ -31,11 +31,11 @@ import {
31
31
  defineAppTool,
32
32
  findCustomTool,
33
33
  isAppToolName
34
- } from "../chunk-AJZ3EJB5.js";
34
+ } from "../chunk-IJAXOQJU.js";
35
35
  import "../chunk-UGWQLQDS.js";
36
36
  import {
37
37
  ToolInputError
38
- } from "../chunk-7BYY7QKC.js";
38
+ } from "../chunk-E65YV7VV.js";
39
39
  export {
40
40
  APP_TOOL_NAMES,
41
41
  DEFAULT_APP_TOOL_PATHS,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  AGENT_PROFILE_EDITOR_SECTIONS,
3
3
  AgentProfileEditor
4
- } from "../chunk-3RHAF2P5.js";
5
- import "../chunk-YESFGM56.js";
6
- import "../chunk-YGBEGBJY.js";
7
- import "../chunk-TJ4DYKF2.js";
4
+ } from "../chunk-A3CUVYRZ.js";
5
+ import "../chunk-5IS3GCMR.js";
6
+ import "../chunk-HEPEGY6Q.js";
7
+ import "../chunk-XG6FODYO.js";
8
8
  import "../chunk-RASGKEAL.js";
9
9
  import "../chunk-VBRUG4N5.js";
10
10
  import "../chunk-B7CL7ENS.js";
@@ -16,7 +16,7 @@ import {
16
16
  resolveAsyncValue,
17
17
  useAsyncResource,
18
18
  useConfirmedMutation
19
- } from "../../chunk-LN5RBVGP.js";
19
+ } from "../../chunk-AVP5SNIG.js";
20
20
  export {
21
21
  AsyncRequestError,
22
22
  AsyncView,
@@ -170,11 +170,10 @@ export declare function pickerRootClass(fullWidth: boolean): string;
170
170
  */
171
171
  export type PickerVariant = 'chip' | 'quiet' | 'field';
172
172
  /**
173
- * Trigger classes for a `field` picker. It matches `@tangle-network/ui`'s
174
- * compact `Input` (height, `rounded-lg`, `--bg-input` well, border hover, and
175
- * `:focus` halo) so a model or harness selector sits in a form as one more
176
- * field. Written out here because `/web-react` controls stay free of the
177
- * sandbox-ui peer.
173
+ * Trigger classes for a `field` picker: `@tangle-network/ui`'s own field
174
+ * presentation and `md` field size, the classes its `Input` and `SelectTrigger`
175
+ * draw, so a model, harness or timezone selector sits in a form as one more
176
+ * field — the same well on every plane, the same border, hover and focus halo.
178
177
  */
179
178
  export declare function fieldPickerTriggerClass({ interactive }?: {
180
179
  interactive?: boolean;
@@ -143,17 +143,17 @@ import {
143
143
  withoutRecordGridCreated,
144
144
  withoutRecordGridRemoved,
145
145
  withoutRecordGridUpdate
146
- } from "../chunk-6O2NQ7WD.js";
146
+ } from "../chunk-Y2SOQOJE.js";
147
+ import {
148
+ MENTION_PILL_CLASS
149
+ } from "../chunk-3HUFJ5LO.js";
147
150
  import {
148
151
  SessionHistoryPanel,
149
152
  formatSessionTimestamp,
150
153
  useInfiniteScroll,
151
154
  useSessionActions,
152
155
  useSessionHistory
153
- } from "../chunk-EQRVOMMS.js";
154
- import {
155
- MENTION_PILL_CLASS
156
- } from "../chunk-3HUFJ5LO.js";
156
+ } from "../chunk-KMIDRUUH.js";
157
157
  import "../chunk-FBVLEGEG.js";
158
158
  import {
159
159
  EvidenceLineageTable,
@@ -180,12 +180,12 @@ import {
180
180
  preloadWhenIdle,
181
181
  useComposerAttachments,
182
182
  useDictation
183
- } from "../chunk-BBHVV37T.js";
183
+ } from "../chunk-MSAGTFN4.js";
184
184
  import "../chunk-L7MB2LLM.js";
185
185
  import {
186
186
  WorkspaceInitial,
187
187
  WorkspaceSwitcher
188
- } from "../chunk-UDLA3NGY.js";
188
+ } from "../chunk-GNBBY3KN.js";
189
189
  import "../chunk-A2GG4D4A.js";
190
190
  import {
191
191
  tabTerminalConnectionId,
@@ -196,7 +196,7 @@ import {
196
196
  AGENT_PROFILE_EDITOR_SECTIONS,
197
197
  AgentProfileEditor,
198
198
  AgentProfileRegistryError
199
- } from "../chunk-3RHAF2P5.js";
199
+ } from "../chunk-A3CUVYRZ.js";
200
200
  import {
201
201
  AgentSessionControls,
202
202
  AgentSettingsPopover,
@@ -206,8 +206,8 @@ import {
206
206
  filterAcceptedFiles,
207
207
  isAcceptedFileType,
208
208
  renamePastedImages
209
- } from "../chunk-YESFGM56.js";
210
- import "../chunk-YGBEGBJY.js";
209
+ } from "../chunk-5IS3GCMR.js";
210
+ import "../chunk-HEPEGY6Q.js";
211
211
  import {
212
212
  DEFAULT_EFFORT_LEVELS,
213
213
  EFFORT_METER_SEGMENTS,
@@ -225,7 +225,7 @@ import {
225
225
  reconcileEffortLevels,
226
226
  usePending,
227
227
  usePopover
228
- } from "../chunk-TJ4DYKF2.js";
228
+ } from "../chunk-XG6FODYO.js";
229
229
  import {
230
230
  parseReviewQueueItem
231
231
  } from "../chunk-GEYACSFW.js";
@@ -236,7 +236,7 @@ import "../chunk-WSEXFTN4.js";
236
236
  import {
237
237
  ProviderLogo
238
238
  } from "../chunk-RASGKEAL.js";
239
- import "../chunk-LN5RBVGP.js";
239
+ import "../chunk-AVP5SNIG.js";
240
240
  import "../chunk-FQYAJYPW.js";
241
241
  import {
242
242
  ATTACHMENT_ACCEPT
@@ -16,11 +16,11 @@ import {
16
16
  } from "../chunk-3NVVBQ2U.js";
17
17
  import {
18
18
  defineAppTool
19
- } from "../chunk-AJZ3EJB5.js";
19
+ } from "../chunk-IJAXOQJU.js";
20
20
  import "../chunk-UGWQLQDS.js";
21
21
  import {
22
22
  ToolInputError
23
- } from "../chunk-7BYY7QKC.js";
23
+ } from "../chunk-E65YV7VV.js";
24
24
  import {
25
25
  isWorkProductStatus,
26
26
  parseAgentCheckInput,
@@ -76,8 +76,8 @@ function WorkProductPane({
76
76
  );
77
77
  const blockingCount = unresolvedBlockingExceptions(workProduct.exceptions).length;
78
78
  return /* @__PURE__ */ jsxs("div", { className: `flex min-h-0 flex-col gap-3 ${className ?? ""}`, children: [
79
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
80
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
79
+ /* @__PURE__ */ jsxs("div", { className: "@container/work-product flex flex-wrap items-center gap-3", children: [
80
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 basis-full @[640px]/work-product:basis-0", children: [
81
81
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
82
82
  /* @__PURE__ */ jsxs("p", { className: "min-w-0 truncate text-sm font-semibold text-foreground", children: [
83
83
  artifact?.title ?? workProduct.scopeKey,
@@ -99,7 +99,7 @@ function WorkProductPane({
99
99
  ] }),
100
100
  /* @__PURE__ */ jsx(ProvenanceStamp, { provenance: workProduct.provenance, backtest })
101
101
  ] }),
102
- /* @__PURE__ */ jsx(PillTabs, { items: tabs, value: tab, onChange: setTab, "aria-label": "Work product views" }),
102
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 overflow-x-auto", children: /* @__PURE__ */ jsx(PillTabs, { items: tabs, value: tab, onChange: setTab, "aria-label": "Work product views" }) }),
103
103
  tab === "artifact" && /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-auto rounded-lg border border-border", children: [
104
104
  artifact?.path && /* @__PURE__ */ jsx(FileBreadcrumb, { path: artifact.path, className: "border-b border-border px-3 py-2" }),
105
105
  body ? /* @__PURE__ */ jsx(CodeSurface, { code: body, filename: diffFilename }) : artifact?.fields ? /* @__PURE__ */ jsx(CodeSurface, { code: JSON.stringify(artifact.fields, null, 2), filename: `${diffFilename}.json`, language: "json" }) : /* @__PURE__ */ jsx("p", { className: "px-4 py-6 text-center text-xs text-muted-foreground", children: "No artifact body yet." })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/work-product-react/index.tsx"],"sourcesContent":["/**\n * `./work-product-react` — the sandbox-ui-composed review pane. It follows the\n * optional-peer rule: this is the ONLY work-product surface\n * that imports `@tangle-network/sandbox-ui` (its `workbench` primitives:\n * `PillTabs`, `CodeSurface`, `DiffView`, `FileBreadcrumb`).\n * `/web-react`'s queue/card/lineage components stay\n * sandbox-ui-free; importing THIS subpath requires the otherwise optional\n * sandbox-ui peer.\n *\n * The pane never fetches: bodies arrive as resolved strings\n * (`currentContent`/`baselineContent`) and history compares load through the\n * injected `loadVersionBody(ref)` seam. The default tab is a PROP — tax\n * passes `'lineage'` (lineage before diff for tax), legal passes `'diff'`\n * (the redline IS the artifact), gtm passes `'artifact'`.\n */\n\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n CodeSurface,\n DiffView,\n FileBreadcrumb,\n PillTabs,\n type PillTabItem,\n} from '@tangle-network/sandbox-ui/workbench'\n\nimport {\n EvidenceLineageTable,\n ExceptionList,\n ProvenanceStamp,\n QualityCheckList,\n WorkProductStatusPill,\n} from '../web-react/work-product'\nimport type {\n EvidenceEntry,\n ProfileBacktestSummary,\n WorkProductRecord,\n WorkProductVersionEntry,\n} from '../work-product/types'\nimport { unresolvedBlockingExceptions } from '../work-product/types'\n\nexport type WorkProductPaneTab = 'artifact' | 'diff' | 'lineage' | 'exceptions' | 'checks' | 'history'\n\n/** Properties for the tabbed work-product review pane */\nexport interface WorkProductPaneProps {\n workProduct: WorkProductRecord\n /** Which tab opens first — a product choice, not a heuristic: tax passes\n * 'lineage', legal 'diff', gtm 'artifact'. Default 'artifact'. */\n defaultTab?: WorkProductPaneTab\n /** Resolved artifact body when it lives at `artifact.path` rather than\n * inline. Falls back to `artifact.content`. */\n currentContent?: string\n /** Resolved baseline body for diff-first products. Falls back to\n * `artifact.baseline.content`. */\n baselineContent?: string\n /** Load a frozen version body by its `history[].artifactPath` ref for the\n * vN-1 → vN compare. Omit to hide history compares. */\n loadVersionBody?: (ref: string) => Promise<string | null>\n /** Resolve one evidence entry's source to an openable URL (lineage\n * click-through). */\n resolveSourceUrl?: (entry: EvidenceEntry) => string\n /** Backtest summary for the record's profile hash, rendered on the\n * provenance stamp. */\n backtest?: ProfileBacktestSummary\n className?: string\n}\n\ninterface VersionCompare {\n fromVersion: number\n toVersion: number\n baseline: string\n current: string\n}\n\n/** Trailing path segment for a diff header. */\nfunction filenameOf(path: string | undefined, fallback: string): string {\n if (!path) return fallback\n const segments = path.split('/')\n return segments[segments.length - 1] || fallback\n}\n\n/**\n * The tabbed review pane over one work product: Artifact | Diff | Lineage |\n * Exceptions | Checks | History. Diff renders baseline→current for redline\n * artifacts and vN-1→vN for history compares; lineage is the\n * every-value-traceable table; provenance is stamped across the header.\n */\nexport function WorkProductPane({\n workProduct,\n defaultTab = 'artifact',\n currentContent,\n baselineContent,\n loadVersionBody,\n resolveSourceUrl,\n backtest,\n className,\n}: WorkProductPaneProps) {\n const artifact = workProduct.artifact\n const body = currentContent ?? artifact?.content ?? ''\n const baseline = baselineContent ?? artifact?.baseline?.content\n const hasDiff = baseline !== undefined\n\n const tabs = useMemo(() => {\n const items: PillTabItem<WorkProductPaneTab>[] = [{ value: 'artifact', label: 'Artifact' }]\n if (hasDiff) items.push({ value: 'diff', label: 'Diff' })\n items.push(\n { value: 'lineage', label: `Lineage (${workProduct.evidence.length})` },\n { value: 'exceptions', label: `Exceptions (${workProduct.exceptions.length})` },\n { value: 'checks', label: `Checks (${workProduct.checks.length})` },\n { value: 'history', label: `History (${workProduct.history.length})` },\n )\n return items\n }, [hasDiff, workProduct.evidence.length, workProduct.exceptions.length, workProduct.checks.length, workProduct.history.length])\n\n const initialTab = tabs.some((tab) => tab.value === defaultTab) ? defaultTab : 'artifact'\n const [tab, setTab] = useState<WorkProductPaneTab>(initialTab)\n const [compare, setCompare] = useState<VersionCompare | null>(null)\n const [compareError, setCompareError] = useState<string | null>(null)\n\n const diffFilename = filenameOf(artifact?.path, artifact?.title ?? workProduct.scopeKey)\n\n const loadCompare = useCallback(\n async (from: WorkProductVersionEntry, to: WorkProductVersionEntry) => {\n if (!loadVersionBody || !from.artifactPath || !to.artifactPath) return\n setCompareError(null)\n try {\n const [fromBody, toBody] = await Promise.all([\n loadVersionBody(from.artifactPath),\n loadVersionBody(to.artifactPath),\n ])\n if (fromBody === null || toBody === null) {\n setCompareError('A version snapshot could not be loaded.')\n return\n }\n setCompare({ fromVersion: from.version, toVersion: to.version, baseline: fromBody, current: toBody })\n } catch (error) {\n setCompareError(error instanceof Error ? error.message : String(error))\n }\n },\n [loadVersionBody],\n )\n\n const blockingCount = unresolvedBlockingExceptions(workProduct.exceptions).length\n\n return (\n <div className={`flex min-h-0 flex-col gap-3 ${className ?? ''}`}>\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <p className=\"min-w-0 truncate text-sm font-semibold text-foreground\">\n {artifact?.title ?? workProduct.scopeKey}\n <span className=\"ml-1.5 text-xs font-normal text-muted-foreground\">v{workProduct.version}</span>\n </p>\n <WorkProductStatusPill status={workProduct.status} size=\"sm\" className=\"shrink-0\" />\n </div>\n {(artifact?.kind || blockingCount > 0) && (\n <p className=\"text-xs text-muted-foreground\">\n {artifact?.kind && <span className=\"font-mono\">{artifact.kind}</span>}\n {artifact?.kind && blockingCount > 0 && ' · '}\n {blockingCount > 0 && <span className=\"text-destructive\">{blockingCount} blocking</span>}\n </p>\n )}\n </div>\n <ProvenanceStamp provenance={workProduct.provenance} backtest={backtest} />\n </div>\n\n <PillTabs items={tabs} value={tab} onChange={setTab} aria-label=\"Work product views\" />\n\n {tab === 'artifact' && (\n <div className=\"min-h-0 flex-1 overflow-auto rounded-lg border border-border\">\n {artifact?.path && <FileBreadcrumb path={artifact.path} className=\"border-b border-border px-3 py-2\" />}\n {body ? (\n <CodeSurface code={body} filename={diffFilename} />\n ) : artifact?.fields ? (\n <CodeSurface code={JSON.stringify(artifact.fields, null, 2)} filename={`${diffFilename}.json`} language=\"json\" />\n ) : (\n <p className=\"px-4 py-6 text-center text-xs text-muted-foreground\">No artifact body yet.</p>\n )}\n </div>\n )}\n\n {tab === 'diff' && hasDiff && (\n <div className=\"min-h-0 flex-1 overflow-auto rounded-lg border border-border\">\n {/* DiffView's own file header already carries the +/− counts. */}\n <DiffView filename={diffFilename} baseline={baseline ?? ''} current={body} />\n </div>\n )}\n\n {tab === 'lineage' && (\n <EvidenceLineageTable evidence={workProduct.evidence} resolveSourceUrl={resolveSourceUrl} />\n )}\n\n {tab === 'exceptions' && <ExceptionList exceptions={workProduct.exceptions} />}\n\n {tab === 'checks' && <QualityCheckList checks={workProduct.checks} />}\n\n {tab === 'history' && (\n <div className=\"space-y-2\">\n {workProduct.history.length === 0 && <p className=\"text-xs text-muted-foreground\">No versions yet.</p>}\n {workProduct.history.map((entry, index) => {\n const prior = workProduct.history\n .slice(0, index)\n .reverse()\n .find((candidate) => candidate.version < entry.version && candidate.artifactPath)\n return (\n <div key={`${entry.version}:${entry.status}:${entry.at}`} className=\"rounded-lg border border-border bg-card px-3 py-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium text-foreground\">v{entry.version}</span>\n <WorkProductStatusPill status={entry.status} size=\"sm\" />\n {entry.reviewedBy && <span className=\"text-xs text-muted-foreground\">by {entry.reviewedBy}</span>}\n <span className=\"flex-1\" />\n {loadVersionBody && prior?.artifactPath && entry.artifactPath && (\n <button\n type=\"button\"\n onClick={() => void loadCompare(prior, entry)}\n className=\"rounded-md border border-border px-2 py-0.5 text-xs font-medium text-foreground transition hover:bg-accent\"\n >\n Compare v{prior.version} → v{entry.version}\n </button>\n )}\n </div>\n {entry.reviewNote && <p className=\"mt-1 text-sm leading-snug text-foreground\">{entry.reviewNote}</p>}\n <ProvenanceStamp provenance={entry.provenance} className=\"mt-1\" />\n </div>\n )\n })}\n {compareError && <p className=\"rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive\">{compareError}</p>}\n {compare && (\n <div className=\"overflow-auto rounded-lg border border-border\">\n <p className=\"border-b border-border px-3 py-2 text-xs text-muted-foreground\">\n v{compare.fromVersion} → v{compare.toVersion}\n </p>\n <DiffView filename={diffFilename} baseline={compare.baseline} current={compare.current} />\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["tab"]}
1
+ {"version":3,"sources":["../../src/work-product-react/index.tsx"],"sourcesContent":["/**\n * `./work-product-react` — the sandbox-ui-composed review pane. It follows the\n * optional-peer rule: this is the ONLY work-product surface\n * that imports `@tangle-network/sandbox-ui` (its `workbench` primitives:\n * `PillTabs`, `CodeSurface`, `DiffView`, `FileBreadcrumb`).\n * `/web-react`'s queue/card/lineage components stay\n * sandbox-ui-free; importing THIS subpath requires the otherwise optional\n * sandbox-ui peer.\n *\n * The pane never fetches: bodies arrive as resolved strings\n * (`currentContent`/`baselineContent`) and history compares load through the\n * injected `loadVersionBody(ref)` seam. The default tab is a PROP — tax\n * passes `'lineage'` (lineage before diff for tax), legal passes `'diff'`\n * (the redline IS the artifact), gtm passes `'artifact'`.\n */\n\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n CodeSurface,\n DiffView,\n FileBreadcrumb,\n PillTabs,\n type PillTabItem,\n} from '@tangle-network/sandbox-ui/workbench'\n\nimport {\n EvidenceLineageTable,\n ExceptionList,\n ProvenanceStamp,\n QualityCheckList,\n WorkProductStatusPill,\n} from '../web-react/work-product'\nimport type {\n EvidenceEntry,\n ProfileBacktestSummary,\n WorkProductRecord,\n WorkProductVersionEntry,\n} from '../work-product/types'\nimport { unresolvedBlockingExceptions } from '../work-product/types'\n\nexport type WorkProductPaneTab = 'artifact' | 'diff' | 'lineage' | 'exceptions' | 'checks' | 'history'\n\n/** Properties for the tabbed work-product review pane */\nexport interface WorkProductPaneProps {\n workProduct: WorkProductRecord\n /** Which tab opens first — a product choice, not a heuristic: tax passes\n * 'lineage', legal 'diff', gtm 'artifact'. Default 'artifact'. */\n defaultTab?: WorkProductPaneTab\n /** Resolved artifact body when it lives at `artifact.path` rather than\n * inline. Falls back to `artifact.content`. */\n currentContent?: string\n /** Resolved baseline body for diff-first products. Falls back to\n * `artifact.baseline.content`. */\n baselineContent?: string\n /** Load a frozen version body by its `history[].artifactPath` ref for the\n * vN-1 → vN compare. Omit to hide history compares. */\n loadVersionBody?: (ref: string) => Promise<string | null>\n /** Resolve one evidence entry's source to an openable URL (lineage\n * click-through). */\n resolveSourceUrl?: (entry: EvidenceEntry) => string\n /** Backtest summary for the record's profile hash, rendered on the\n * provenance stamp. */\n backtest?: ProfileBacktestSummary\n className?: string\n}\n\ninterface VersionCompare {\n fromVersion: number\n toVersion: number\n baseline: string\n current: string\n}\n\n/** Trailing path segment for a diff header. */\nfunction filenameOf(path: string | undefined, fallback: string): string {\n if (!path) return fallback\n const segments = path.split('/')\n return segments[segments.length - 1] || fallback\n}\n\n/**\n * The tabbed review pane over one work product: Artifact | Diff | Lineage |\n * Exceptions | Checks | History. Diff renders baseline→current for redline\n * artifacts and vN-1→vN for history compares; lineage is the\n * every-value-traceable table; provenance is stamped across the header.\n */\nexport function WorkProductPane({\n workProduct,\n defaultTab = 'artifact',\n currentContent,\n baselineContent,\n loadVersionBody,\n resolveSourceUrl,\n backtest,\n className,\n}: WorkProductPaneProps) {\n const artifact = workProduct.artifact\n const body = currentContent ?? artifact?.content ?? ''\n const baseline = baselineContent ?? artifact?.baseline?.content\n const hasDiff = baseline !== undefined\n\n const tabs = useMemo(() => {\n const items: PillTabItem<WorkProductPaneTab>[] = [{ value: 'artifact', label: 'Artifact' }]\n if (hasDiff) items.push({ value: 'diff', label: 'Diff' })\n items.push(\n { value: 'lineage', label: `Lineage (${workProduct.evidence.length})` },\n { value: 'exceptions', label: `Exceptions (${workProduct.exceptions.length})` },\n { value: 'checks', label: `Checks (${workProduct.checks.length})` },\n { value: 'history', label: `History (${workProduct.history.length})` },\n )\n return items\n }, [hasDiff, workProduct.evidence.length, workProduct.exceptions.length, workProduct.checks.length, workProduct.history.length])\n\n const initialTab = tabs.some((tab) => tab.value === defaultTab) ? defaultTab : 'artifact'\n const [tab, setTab] = useState<WorkProductPaneTab>(initialTab)\n const [compare, setCompare] = useState<VersionCompare | null>(null)\n const [compareError, setCompareError] = useState<string | null>(null)\n\n const diffFilename = filenameOf(artifact?.path, artifact?.title ?? workProduct.scopeKey)\n\n const loadCompare = useCallback(\n async (from: WorkProductVersionEntry, to: WorkProductVersionEntry) => {\n if (!loadVersionBody || !from.artifactPath || !to.artifactPath) return\n setCompareError(null)\n try {\n const [fromBody, toBody] = await Promise.all([\n loadVersionBody(from.artifactPath),\n loadVersionBody(to.artifactPath),\n ])\n if (fromBody === null || toBody === null) {\n setCompareError('A version snapshot could not be loaded.')\n return\n }\n setCompare({ fromVersion: from.version, toVersion: to.version, baseline: fromBody, current: toBody })\n } catch (error) {\n setCompareError(error instanceof Error ? error.message : String(error))\n }\n },\n [loadVersionBody],\n )\n\n const blockingCount = unresolvedBlockingExceptions(workProduct.exceptions).length\n\n return (\n <div className={`flex min-h-0 flex-col gap-3 ${className ?? ''}`}>\n {/* Stack on the pane's own width: embedded panes are narrow on wide screens too. */}\n <div className=\"@container/work-product flex flex-wrap items-center gap-3\">\n <div className=\"min-w-0 flex-1 basis-full @[640px]/work-product:basis-0\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <p className=\"min-w-0 truncate text-sm font-semibold text-foreground\">\n {artifact?.title ?? workProduct.scopeKey}\n <span className=\"ml-1.5 text-xs font-normal text-muted-foreground\">v{workProduct.version}</span>\n </p>\n <WorkProductStatusPill status={workProduct.status} size=\"sm\" className=\"shrink-0\" />\n </div>\n {(artifact?.kind || blockingCount > 0) && (\n <p className=\"text-xs text-muted-foreground\">\n {artifact?.kind && <span className=\"font-mono\">{artifact.kind}</span>}\n {artifact?.kind && blockingCount > 0 && ' · '}\n {blockingCount > 0 && <span className=\"text-destructive\">{blockingCount} blocking</span>}\n </p>\n )}\n </div>\n <ProvenanceStamp provenance={workProduct.provenance} backtest={backtest} />\n </div>\n\n <div className=\"min-w-0 overflow-x-auto\">\n <PillTabs items={tabs} value={tab} onChange={setTab} aria-label=\"Work product views\" />\n </div>\n\n {tab === 'artifact' && (\n <div className=\"min-h-0 flex-1 overflow-auto rounded-lg border border-border\">\n {artifact?.path && <FileBreadcrumb path={artifact.path} className=\"border-b border-border px-3 py-2\" />}\n {body ? (\n <CodeSurface code={body} filename={diffFilename} />\n ) : artifact?.fields ? (\n <CodeSurface code={JSON.stringify(artifact.fields, null, 2)} filename={`${diffFilename}.json`} language=\"json\" />\n ) : (\n <p className=\"px-4 py-6 text-center text-xs text-muted-foreground\">No artifact body yet.</p>\n )}\n </div>\n )}\n\n {tab === 'diff' && hasDiff && (\n <div className=\"min-h-0 flex-1 overflow-auto rounded-lg border border-border\">\n {/* DiffView's own file header already carries the +/− counts. */}\n <DiffView filename={diffFilename} baseline={baseline ?? ''} current={body} />\n </div>\n )}\n\n {tab === 'lineage' && (\n <EvidenceLineageTable evidence={workProduct.evidence} resolveSourceUrl={resolveSourceUrl} />\n )}\n\n {tab === 'exceptions' && <ExceptionList exceptions={workProduct.exceptions} />}\n\n {tab === 'checks' && <QualityCheckList checks={workProduct.checks} />}\n\n {tab === 'history' && (\n <div className=\"space-y-2\">\n {workProduct.history.length === 0 && <p className=\"text-xs text-muted-foreground\">No versions yet.</p>}\n {workProduct.history.map((entry, index) => {\n const prior = workProduct.history\n .slice(0, index)\n .reverse()\n .find((candidate) => candidate.version < entry.version && candidate.artifactPath)\n return (\n <div key={`${entry.version}:${entry.status}:${entry.at}`} className=\"rounded-lg border border-border bg-card px-3 py-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium text-foreground\">v{entry.version}</span>\n <WorkProductStatusPill status={entry.status} size=\"sm\" />\n {entry.reviewedBy && <span className=\"text-xs text-muted-foreground\">by {entry.reviewedBy}</span>}\n <span className=\"flex-1\" />\n {loadVersionBody && prior?.artifactPath && entry.artifactPath && (\n <button\n type=\"button\"\n onClick={() => void loadCompare(prior, entry)}\n className=\"rounded-md border border-border px-2 py-0.5 text-xs font-medium text-foreground transition hover:bg-accent\"\n >\n Compare v{prior.version} → v{entry.version}\n </button>\n )}\n </div>\n {entry.reviewNote && <p className=\"mt-1 text-sm leading-snug text-foreground\">{entry.reviewNote}</p>}\n <ProvenanceStamp provenance={entry.provenance} className=\"mt-1\" />\n </div>\n )\n })}\n {compareError && <p className=\"rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive\">{compareError}</p>}\n {compare && (\n <div className=\"overflow-auto rounded-lg border border-border\">\n <p className=\"border-b border-border px-3 py-2 text-xs text-muted-foreground\">\n v{compare.fromVersion} → v{compare.toVersion}\n </p>\n <DiffView filename={diffFilename} baseline={compare.baseline} current={compare.current} />\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["tab"]}
@@ -5,6 +5,7 @@
5
5
  * a running export.
6
6
  */
7
7
  import { type ReactNode } from 'react';
8
+ import type { ExportProgress } from './types';
8
9
  export interface WorkspaceExportPanelProps {
9
10
  /** The mount path of the product's export routes, e.g. `/api/workspaces/ws-1/exports`. */
10
11
  endpoint: string;
@@ -18,4 +19,29 @@ export interface WorkspaceExportPanelProps {
18
19
  /** True when the product runs `continueRunning` on a cron, so the export finishes after the page closes. */
19
20
  continuesInBackground?: boolean;
20
21
  }
22
+ export interface UseWorkspaceExportOptions {
23
+ /** The mount path of the product's export routes. */
24
+ endpoint: string;
25
+ /** False skips loading; the server refuses non-owners regardless. */
26
+ enabled: boolean;
27
+ fetcher?: typeof fetch;
28
+ }
29
+ export interface WorkspaceExportState {
30
+ /** The newest export, if any. */
31
+ latest: ExportProgress | undefined;
32
+ /** The last request failure, as a sentence. */
33
+ error: string | null;
34
+ loading: boolean;
35
+ starting: boolean;
36
+ running: boolean;
37
+ start: () => Promise<void>;
38
+ /** Resumes a running export, or reloads the list. */
39
+ retry: () => void;
40
+ }
41
+ /**
42
+ * Starts an export, drives it to completion by calling `advance` while the
43
+ * page is open, and resumes a running one on mount. `WorkspaceExportPanel`
44
+ * and the settings `ExportSection` both draw from it.
45
+ */
46
+ export declare function useWorkspaceExport({ endpoint, enabled, fetcher }: UseWorkspaceExportOptions): WorkspaceExportState;
21
47
  export declare function WorkspaceExportPanel({ endpoint, canExport, title, description, className, fetcher, continuesInBackground }: WorkspaceExportPanelProps): import("react").JSX.Element | null;