@opengeni/react 7.6.1-canary.37073905159001 → 7.6.1

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 (136) hide show
  1. package/README.md +2 -19
  2. package/dist/{browser-viewer-63TXMINB.js → browser-viewer-5X47I6T2.js} +5 -6
  3. package/dist/{chunk-TKB4SB44.js → chunk-45GE35O7.js} +2 -10
  4. package/dist/chunk-45GE35O7.js.map +1 -0
  5. package/dist/{chunk-DOJWJQ3F.js → chunk-5EXGU7UF.js} +3 -3
  6. package/dist/{chunk-YN7I3STW.js → chunk-6NEZPF53.js} +32 -153
  7. package/dist/chunk-6NEZPF53.js.map +1 -0
  8. package/dist/{chunk-I7IWRQLN.js → chunk-BI57CQUV.js} +2 -2
  9. package/dist/{chunk-6ZSWSH4I.js → chunk-CHPKSLPN.js} +1 -3
  10. package/dist/chunk-CHPKSLPN.js.map +1 -0
  11. package/dist/{chunk-Q5QBEZIQ.js → chunk-DV2DBNPQ.js} +29 -48
  12. package/dist/chunk-DV2DBNPQ.js.map +1 -0
  13. package/dist/{chunk-RDKBXMWX.js → chunk-FGBCYQOT.js} +74 -120
  14. package/dist/chunk-FGBCYQOT.js.map +1 -0
  15. package/dist/{chunk-AAG4R3QN.js → chunk-KGENSZKS.js} +3 -4
  16. package/dist/chunk-KGENSZKS.js.map +1 -0
  17. package/dist/{chunk-YQEAA6LT.js → chunk-KZ73RL76.js} +3 -7
  18. package/dist/chunk-KZ73RL76.js.map +1 -0
  19. package/dist/{chunk-LFXYGZWK.js → chunk-LZ3KHA55.js} +60 -169
  20. package/dist/chunk-LZ3KHA55.js.map +1 -0
  21. package/dist/{chunk-2QUSQVF2.js → chunk-UINDK6C3.js} +3 -3
  22. package/dist/{chunk-UTRTG6LH.js → chunk-UJRYEWZI.js} +8 -48
  23. package/dist/chunk-UJRYEWZI.js.map +1 -0
  24. package/dist/{chunk-XOAMRDDK.js → chunk-VGIKFIO2.js} +3 -6
  25. package/dist/chunk-VGIKFIO2.js.map +1 -0
  26. package/dist/{chunk-ELCE5SXV.js → chunk-WZ55DVGK.js} +4 -8
  27. package/dist/chunk-WZ55DVGK.js.map +1 -0
  28. package/dist/{chunk-IQUCRAT7.js → chunk-XA6R7DJQ.js} +3 -3
  29. package/dist/{chunk-TOAH2BKA.js → chunk-YCGBREUV.js} +2 -6
  30. package/dist/{chunk-TOAH2BKA.js.map → chunk-YCGBREUV.js.map} +1 -1
  31. package/dist/{chunk-HNFCAQ5R.js → chunk-YRYF2GLG.js} +31 -39
  32. package/dist/chunk-YRYF2GLG.js.map +1 -0
  33. package/dist/{chunk-GISEVGS6.js → chunk-YSMMDOOE.js} +2 -10
  34. package/dist/chunk-YSMMDOOE.js.map +1 -0
  35. package/dist/{chunk-CWWKTNKK.js → chunk-Z3DISEQN.js} +18 -35
  36. package/dist/chunk-Z3DISEQN.js.map +1 -0
  37. package/dist/client.d.ts +1 -4
  38. package/dist/commands/types.d.ts +0 -2
  39. package/dist/components/approval-surface.d.ts +0 -15
  40. package/dist/composer.js +7 -8
  41. package/dist/{computer-viewer-X2MBRZY4.js → computer-viewer-7GF35SNY.js} +4 -5
  42. package/dist/connect.js +3 -3
  43. package/dist/hooks/use-browser-session.d.ts +0 -4
  44. package/dist/hooks/use-composer.d.ts +0 -2
  45. package/dist/hooks/use-computer-session.d.ts +0 -2
  46. package/dist/hooks/use-file-attachments.d.ts +0 -2
  47. package/dist/hooks/use-session-events.d.ts +1 -1
  48. package/dist/hooks/use-workspace-sessions.d.ts +5 -15
  49. package/dist/index.d.ts +2 -2
  50. package/dist/index.js +21 -28
  51. package/dist/index.js.map +1 -1
  52. package/dist/interaction.d.ts +1 -1
  53. package/dist/interaction.js +5 -6
  54. package/dist/interaction.js.map +1 -1
  55. package/dist/{latest-question-CNEZ5KAN.js → latest-question-3BJGKQQ6.js} +3 -3
  56. package/dist/lib/format.d.ts +3 -27
  57. package/dist/lib/interaction-errors.d.ts +0 -3
  58. package/dist/machines.js +4 -5
  59. package/dist/{model-policy-picker-menu-JSA64TNN.js → model-policy-picker-menu-PUTA5JOB.js} +4 -4
  60. package/dist/model-policy.js +1 -1
  61. package/dist/provider.d.ts +1 -5
  62. package/dist/{queue-surface-implementation-HENWYFWA.js → queue-surface-implementation-2CAQAIHM.js} +2 -3
  63. package/dist/{queue-surface-implementation-HENWYFWA.js.map → queue-surface-implementation-2CAQAIHM.js.map} +1 -1
  64. package/dist/realtime.js +2 -2
  65. package/dist/{sandbox-files-BBC47SLD.js → sandbox-files-AEJTWGT4.js} +3 -4
  66. package/dist/session-ui.d.ts +1 -1
  67. package/dist/session-ui.js +12 -13
  68. package/dist/session-ui.js.map +1 -1
  69. package/dist/session.js +6 -7
  70. package/dist/usage.js +1 -2
  71. package/dist/usage.js.map +1 -1
  72. package/package.json +3 -3
  73. package/src/client.ts +1 -5
  74. package/src/commands/registry.ts +15 -18
  75. package/src/commands/types.ts +0 -2
  76. package/src/components/approval-surface.tsx +42 -166
  77. package/src/components/browser-viewer.tsx +9 -38
  78. package/src/components/composer.tsx +2 -9
  79. package/src/components/computer-viewer.tsx +30 -160
  80. package/src/components/generated-video-player.tsx +2 -4
  81. package/src/components/human-input-form.tsx +5 -18
  82. package/src/components/message-timeline.tsx +2 -4
  83. package/src/components/open-geni-chat.tsx +4 -11
  84. package/src/components/queue-surface-state.tsx +1 -3
  85. package/src/components/session-chrome.tsx +2 -5
  86. package/src/components/session-commands-panel.tsx +1 -3
  87. package/src/components/session-conversation.tsx +2 -4
  88. package/src/components/session-list.tsx +5 -7
  89. package/src/components/session-mcp-capability-card.tsx +1 -11
  90. package/src/hooks/internal.ts +2 -3
  91. package/src/hooks/use-browser-session.ts +79 -115
  92. package/src/hooks/use-composer.ts +31 -70
  93. package/src/hooks/use-computer-session.ts +6 -57
  94. package/src/hooks/use-file-attachments.ts +3 -16
  95. package/src/hooks/use-goal.ts +1 -2
  96. package/src/hooks/use-session-capabilities.ts +2 -3
  97. package/src/hooks/use-session-events.ts +8 -15
  98. package/src/hooks/use-slash-commands.ts +11 -6
  99. package/src/hooks/use-turn-queue.ts +1 -2
  100. package/src/hooks/use-workspace-sessions.ts +7 -40
  101. package/src/index.ts +2 -7
  102. package/src/interaction.ts +0 -1
  103. package/src/lib/format.ts +9 -73
  104. package/src/lib/interaction-errors.ts +0 -15
  105. package/src/model-policy.ts +0 -5
  106. package/src/provider.tsx +4 -12
  107. package/src/session-ui.ts +1 -1
  108. package/src/timeline/projection.ts +1 -12
  109. package/styles/compiled.css +0 -14
  110. package/dist/chunk-6ZSWSH4I.js.map +0 -1
  111. package/dist/chunk-AAG4R3QN.js.map +0 -1
  112. package/dist/chunk-CWWKTNKK.js.map +0 -1
  113. package/dist/chunk-ELCE5SXV.js.map +0 -1
  114. package/dist/chunk-GISEVGS6.js.map +0 -1
  115. package/dist/chunk-HNFCAQ5R.js.map +0 -1
  116. package/dist/chunk-LFXYGZWK.js.map +0 -1
  117. package/dist/chunk-Q5QBEZIQ.js.map +0 -1
  118. package/dist/chunk-RDKBXMWX.js.map +0 -1
  119. package/dist/chunk-TKB4SB44.js.map +0 -1
  120. package/dist/chunk-UGLEYEMP.js +0 -33
  121. package/dist/chunk-UGLEYEMP.js.map +0 -1
  122. package/dist/chunk-UTRTG6LH.js.map +0 -1
  123. package/dist/chunk-XOAMRDDK.js.map +0 -1
  124. package/dist/chunk-YN7I3STW.js.map +0 -1
  125. package/dist/chunk-YQEAA6LT.js.map +0 -1
  126. package/dist/lib/error-message.d.ts +0 -7
  127. package/src/lib/error-message.ts +0 -37
  128. /package/dist/{browser-viewer-63TXMINB.js.map → browser-viewer-5X47I6T2.js.map} +0 -0
  129. /package/dist/{chunk-DOJWJQ3F.js.map → chunk-5EXGU7UF.js.map} +0 -0
  130. /package/dist/{chunk-I7IWRQLN.js.map → chunk-BI57CQUV.js.map} +0 -0
  131. /package/dist/{chunk-2QUSQVF2.js.map → chunk-UINDK6C3.js.map} +0 -0
  132. /package/dist/{chunk-IQUCRAT7.js.map → chunk-XA6R7DJQ.js.map} +0 -0
  133. /package/dist/{computer-viewer-X2MBRZY4.js.map → computer-viewer-7GF35SNY.js.map} +0 -0
  134. /package/dist/{latest-question-CNEZ5KAN.js.map → latest-question-3BJGKQQ6.js.map} +0 -0
  135. /package/dist/{model-policy-picker-menu-JSA64TNN.js.map → model-policy-picker-menu-PUTA5JOB.js.map} +0 -0
  136. /package/dist/{sandbox-files-BBC47SLD.js.map → sandbox-files-AEJTWGT4.js.map} +0 -0
package/dist/usage.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/use-usage.ts","../src/usage/summary.ts","../src/components/usage-meter.tsx","../src/components/usage-limit-notice.tsx","../src/components/usage-member-list.tsx"],"sourcesContent":["import type { OpenGeniClient } from \"@opengeni/sdk\";\nimport {\n getMyUsage,\n type UsageAllowancePeriod,\n type WorkspaceUsageResponse,\n} from \"@opengeni/sdk/usage-allowances\";\nimport { useCallback, useContext, useEffect, useMemo, useRef } from \"react\";\n\nimport { OpenGeniContext } from \"../session-context\";\nimport { summarizeUsage, type UsageSummary } from \"../usage/summary\";\nimport { usePolledValue } from \"./internal\";\n\n/**\n * Anything that can read the signed-in member's own usage: the root SDK client\n * pointed at your session proxy (`getMyUsage`), or any client exposing\n * `requestJson` (for example the narrower browser client).\n */\nexport type UsageClientLike =\n | Pick<OpenGeniClient, \"getMyUsage\">\n | Pick<OpenGeniClient, \"requestJson\">;\n\nexport type UseUsageOptions = {\n /** Defaults to the `<OpenGeniProvider>` client. */\n client?: UsageClientLike | undefined;\n /** Defaults to the `<OpenGeniProvider>` workspace. */\n workspaceId?: string | undefined;\n /** `\"current\"` (default) or a `YYYY-MM` allowance period. */\n period?: UsageAllowancePeriod | undefined;\n /** Re-read on an interval. Off by default; usage changes only when work settles. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n /**\n * Re-read whenever this value changes, keeping the current reading while it\n * loads. Pass something that changes when work settles (a turn count, the\n * session status) so the meter follows real usage without polling.\n */\n refreshKey?: unknown;\n};\n\nexport type UseUsageResult = {\n /** The raw `/usage/me` response: the workspace pool and your own row. */\n usage: WorkspaceUsageResponse | null;\n /** What to tell this member; null until loaded. */\n summary: UsageSummary | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nfunction readOwnUsage(\n client: UsageClientLike,\n workspaceId: string,\n period: UsageAllowancePeriod | undefined,\n signal: AbortSignal | undefined,\n): Promise<WorkspaceUsageResponse> {\n const query = period === undefined ? {} : { period };\n const options = signal ? { signal } : {};\n if (\"getMyUsage\" in client && typeof client.getMyUsage === \"function\") {\n return client.getMyUsage(workspaceId, query, options);\n }\n if (\"requestJson\" in client && typeof client.requestJson === \"function\") {\n return getMyUsage(client, workspaceId, query, options);\n }\n throw new Error(\"@opengeni/react: the usage client needs getMyUsage or requestJson.\");\n}\n\n/**\n * The signed-in member's own usage in one workspace, read through `/usage/me`\n * (the session proxy serves exactly this route). Never reads another member.\n */\nexport function useUsage(options: UseUsageOptions = {}): UseUsageResult {\n const context = useContext(OpenGeniContext);\n const client = (options.client ?? context?.client) as UsageClientLike | undefined;\n const workspaceId = options.workspaceId ?? context?.workspaceId;\n if (!client || !workspaceId) {\n throw new Error(\n \"@opengeni/react: useUsage needs a client and workspace. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId }.\",\n );\n }\n const period = options.period;\n const load = useCallback(\n (signal?: AbortSignal) => readOwnUsage(client, workspaceId, period, signal),\n [client, workspaceId, period],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const refreshKey = options.refreshKey;\n const seenKey = useRef(refreshKey);\n const refresh = state.refresh;\n useEffect(() => {\n if (Object.is(seenKey.current, refreshKey)) return;\n seenKey.current = refreshKey;\n void refresh();\n }, [refreshKey, refresh]);\n const usage = state.data;\n const summary = useMemo(() => (usage ? summarizeUsage(usage) : null), [usage]);\n return { usage, summary, loading: state.loading, error: state.error, refresh };\n}\n","import type {\n MemberAllowanceUsage,\n UsageAllowanceStatus,\n WorkspaceAllowanceUsage,\n WorkspaceUsageResponse,\n} from \"@opengeni/sdk/usage-allowances\";\n\nimport type { AllowanceScope } from \"./allowance-copy\";\n\n/** One ceiling as a person reads it: how much of it is used, and when it resets. */\nexport type UsageReading = {\n scope: AllowanceScope;\n /** Used share of the ceiling. Can exceed 1 after post-call overshoot. */\n fraction: number;\n status: UsageAllowanceStatus;\n resetsAt: string | null;\n /** USD micros. Only shown when a host opts in with `formatAmount`. */\n used: number;\n limit: number;\n remaining: number;\n};\n\n/**\n * What a member can still do: `unlimited` when no ceiling applies, otherwise\n * the state of whichever ceiling stops them first (`binding`).\n */\nexport type UsageSummary = {\n state: \"unlimited\" | \"ok\" | \"warning\" | \"exhausted\";\n /** The ceiling a person would ask to raise; null when unlimited. */\n binding: UsageReading | null;\n /** Their own ceiling, when one applies. */\n member: UsageReading | null;\n /** The shared workspace pool, when one is set. */\n workspace: UsageReading | null;\n};\n\nfunction reading(\n scope: AllowanceScope,\n usage: Pick<\n WorkspaceAllowanceUsage | MemberAllowanceUsage,\n \"limit\" | \"used\" | \"remaining\" | \"fraction\" | \"status\" | \"resetsAt\"\n >,\n): UsageReading | null {\n if (usage.limit === null) return null;\n return {\n scope,\n fraction: usage.fraction ?? (usage.limit === 0 ? 1 : usage.used / usage.limit),\n status: usage.status,\n resetsAt: usage.resetsAt,\n used: usage.used,\n limit: usage.limit,\n remaining: Math.max(0, usage.remaining ?? usage.limit - usage.used),\n };\n}\n\n/**\n * Fold a usage response (normally `/usage/me`) into what to tell one member.\n * The server's `status` decides warning/exhausted (it applies the configured\n * thresholds); when both ceilings apply, the one with less room left binds.\n */\nexport function summarizeUsage(\n usage: WorkspaceUsageResponse,\n subjectId?: string | undefined,\n): UsageSummary {\n const row =\n subjectId === undefined\n ? usage.members[0]\n : usage.members.find((member) => member.subjectId === subjectId);\n const member = row ? reading(\"member\", row) : null;\n const workspace = reading(\"workspace\", usage.workspace);\n const readings = [member, workspace].filter((value): value is UsageReading => value !== null);\n if (readings.length === 0) return { state: \"unlimited\", binding: null, member, workspace };\n // Raising a member ceiling cannot help while the shared pool is spent, so an\n // exhausted workspace binds first.\n const exhausted =\n (workspace?.status === \"exhausted\" ? workspace : null) ??\n (member?.status === \"exhausted\" ? member : null);\n if (exhausted) return { state: \"exhausted\", binding: exhausted, member, workspace };\n const warnings = readings.filter((value) => value.status === \"warning\");\n const candidates = warnings.length > 0 ? warnings : readings;\n const binding = candidates.reduce((tightest, value) =>\n value.remaining < tightest.remaining ? value : tightest,\n );\n return { state: warnings.length > 0 ? \"warning\" : \"ok\", binding, member, workspace };\n}\n\n/** A whole-number percentage for display: \"<1\" for a sliver, never \"0\" for some use. */\nexport function usagePercentLabel(fraction: number): string {\n if (!Number.isFinite(fraction) || fraction <= 0) return \"0\";\n if (fraction < 0.01) return \"<1\";\n if (fraction < 1 && Math.round(fraction * 100) === 100) return \"99\";\n return String(Math.round(fraction * 100));\n}\n","import type { UsageAllowancePeriod, WorkspaceUsageResponse } from \"@opengeni/sdk/usage-allowances\";\nimport type { ReactNode } from \"react\";\n\nimport { useUsage, type UsageClientLike } from \"../hooks/use-usage\";\nimport { cn } from \"../lib/cn\";\nimport {\n allowanceLabels,\n allowanceResetSentence,\n formatAllowanceInstant,\n type AllowanceLabels,\n} from \"../usage/allowance-copy\";\nimport {\n summarizeUsage,\n usagePercentLabel,\n type UsageReading,\n type UsageSummary,\n} from \"../usage/summary\";\n\nexport type UsageMeterLabels = AllowanceLabels & {\n /** Heading for the member's own ceiling. */\n title: string;\n /** Heading when only the shared workspace pool applies. */\n workspaceTitle: string;\n /** \"38% left\" — `percent` is preformatted (\"38\", \"<1\"). */\n left: (percent: string) => string;\n /** \"62% used\". */\n used: (percent: string) => string;\n limitReached: string;\n /** Status chip beside a hero meter that is close to its limit. */\n nearLimit: string;\n /** \"$13.00 left\" for a hero meter with amounts. */\n amountLeft: (amount: string) => string;\n /** No ceiling applies to this member. */\n unlimited: string;\n loading: string;\n error: string;\n retry: string;\n /** \"$31.00 of $50.00 used\" when the host passes `formatAmount`. */\n amounts: (used: string, limit: string) => string;\n /** The shared pool, under a member's own meter: \"Workspace: 9% left\". */\n workspaceLine: (value: string) => string;\n};\n\nexport const DEFAULT_USAGE_METER_LABELS: UsageMeterLabels = {\n ...allowanceLabels(),\n title: \"Your usage\",\n workspaceTitle: \"Workspace usage\",\n left: (percent) => `${percent}% left`,\n used: (percent) => `${percent}% used`,\n limitReached: \"Limit reached\",\n nearLimit: \"Near limit\",\n amountLeft: (amount) => `${amount} left`,\n unlimited: \"No usage limit\",\n loading: \"Checking usage…\",\n error: \"Couldn't load usage\",\n retry: \"Try again\",\n amounts: (used, limit) => `${used} of ${limit} used`,\n workspaceLine: (value) => `Workspace: ${value}`,\n};\n\nexport type UsageMeterBaseProps = {\n /** \"left\" (default) reads like a battery; \"used\" reads like spend. */\n measure?: \"left\" | \"used\" | undefined;\n /**\n * Opt in to amounts. Receives USD micros; return your own unit (\"$31.00\",\n * \"3.1 credits\", \"1.5× plan\"). Without it the meter shows only shares.\n */\n formatAmount?: ((micros: number) => string) | undefined;\n labels?: Partial<UsageMeterLabels> | undefined;\n /**\n * `compact` is one line for menus and headers; `hero` leads a page with the\n * amount (or share) left in large type. An empty `labels.title` hides the\n * heading when the page already names it.\n */\n density?: \"default\" | \"compact\" | \"hero\" | undefined;\n /** Show the shared pool under the member's own meter when it is the tighter one. Default true. */\n showWorkspace?: boolean | undefined;\n className?: string | undefined;\n};\n\nexport type UsageMeterProps = UsageMeterBaseProps & {\n /**\n * A usage response you already loaded (for example from `useUsage`).\n * Omit it to let the meter read `/usage/me` itself.\n */\n usage?: WorkspaceUsageResponse | null | undefined;\n client?: UsageClientLike | undefined;\n workspaceId?: string | undefined;\n period?: UsageAllowancePeriod | undefined;\n /** Re-read when this changes (see `useUsage`). */\n refreshKey?: unknown;\n};\n\n/**\n * The signed-in member's own usage: how much of their limit is left and when\n * it resets. Shares only by default; hosts opt in to amounts.\n */\nexport function UsageMeter({\n usage,\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: UsageMeterProps) {\n if (usage !== undefined) {\n return (\n <UsageMeterView\n {...view}\n summary={usage ? summarizeUsage(usage) : null}\n loading={usage === null}\n />\n );\n }\n return (\n <LoadingUsageMeter\n {...view}\n client={client}\n workspaceId={workspaceId}\n period={period}\n refreshKey={refreshKey}\n />\n );\n}\n\nfunction LoadingUsageMeter({\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: UsageMeterBaseProps &\n Pick<UsageMeterProps, \"client\" | \"workspaceId\" | \"period\" | \"refreshKey\">) {\n const state = useUsage({ client, workspaceId, period, refreshKey });\n return (\n <UsageMeterView\n {...view}\n summary={state.summary}\n loading={state.loading && !state.summary}\n error={state.summary ? null : state.error}\n onRetry={() => void state.refresh()}\n />\n );\n}\n\nexport type UsageMeterViewProps = UsageMeterBaseProps & {\n summary: UsageSummary | null;\n loading?: boolean | undefined;\n error?: Error | null | undefined;\n onRetry?: (() => void) | undefined;\n};\n\nconst FILL: Record<UsageReading[\"status\"], string> = {\n ok: \"bg-og-accent\",\n warning: \"bg-og-status-waiting\",\n exhausted: \"bg-og-danger\",\n};\n\nfunction valueText(\n reading: UsageReading,\n measure: \"left\" | \"used\",\n labels: UsageMeterLabels,\n): string {\n if (reading.status === \"exhausted\") return labels.limitReached;\n return measure === \"left\"\n ? labels.left(usagePercentLabel(Math.max(0, 1 - reading.fraction)))\n : labels.used(usagePercentLabel(reading.fraction));\n}\n\n/** Presentational meter for a summary you computed (`summarizeUsage`). */\nexport function UsageMeterView({\n summary,\n loading = false,\n error = null,\n onRetry,\n measure = \"left\",\n formatAmount,\n labels: overrides,\n density = \"default\",\n showWorkspace = true,\n className,\n}: UsageMeterViewProps) {\n const labels = { ...DEFAULT_USAGE_METER_LABELS, ...overrides };\n const compact = density === \"compact\";\n if (loading || error || !summary) {\n return (\n <div\n className={cn(\"og-root min-w-0 text-og-sm\", className)}\n data-og-usage-meter={error ? \"error\" : \"loading\"}\n aria-busy={loading || undefined}\n >\n {error ? (\n <p className=\"flex flex-wrap items-center gap-x-2 text-og-fg-muted\" role=\"alert\">\n {labels.error}\n {onRetry ? (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-og-sm text-og-fg underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-og-accent\"\n >\n {labels.retry}\n </button>\n ) : null}\n </p>\n ) : (\n <span className=\"text-og-fg-subtle\">{labels.loading}</span>\n )}\n </div>\n );\n }\n const primary = summary.member ?? summary.workspace;\n if (!primary) {\n return (\n <div\n className={cn(\"og-root min-w-0 text-og-sm text-og-fg-muted\", className)}\n data-og-usage-meter=\"unlimited\"\n >\n {compact ? null : <span className=\"font-medium text-og-fg\">{labels.title} · </span>}\n {labels.unlimited}\n </div>\n );\n }\n const title = summary.member ? labels.title : labels.workspaceTitle;\n const value = valueText(primary, measure, labels);\n // The bar always fills with what's used, so \"near the limit\" reads as a\n // nearly full bar whichever way the words count.\n const fill = Math.min(1, Math.max(0, primary.fraction));\n const reset = allowanceResetSentence(labels, primary.resetsAt);\n const pool =\n showWorkspace &&\n summary.member &&\n summary.workspace &&\n (summary.binding?.scope === \"workspace\" || summary.workspace.status !== \"ok\")\n ? summary.workspace\n : null;\n const meterProps = {\n role: \"meter\" as const,\n \"aria-label\": title || labels.title,\n \"aria-valuemin\": 0,\n \"aria-valuemax\": 100,\n \"aria-valuenow\": Math.round(fill * 100),\n \"aria-valuetext\": `${value}. ${reset}${\n pool ? ` ${labels.workspaceLine(valueText(pool, measure, labels))}.` : \"\"\n }`,\n };\n const bar = (\n <span\n aria-hidden\n className={cn(\n \"relative block overflow-hidden rounded-full\",\n density === \"hero\" ? \"h-1.5\" : \"h-1\",\n primary.status === \"exhausted\" ? \"bg-og-danger/15\" : \"bg-og-surface-3\",\n compact ? \"w-16 shrink-0\" : \"w-full\",\n )}\n >\n <span\n className={cn(\"absolute inset-y-0 left-0 rounded-full\", FILL[primary.status])}\n style={{ width: `${Math.round(fill * 1000) / 10}%` }}\n />\n </span>\n );\n const valueClass = cn(\n \"shrink-0 whitespace-nowrap tabular-nums\",\n primary.status === \"ok\" ? \"text-og-fg-muted\" : \"font-medium\",\n primary.status === \"warning\" && \"text-og-status-waiting\",\n primary.status === \"exhausted\" && \"text-og-danger\",\n );\n if (compact) {\n return (\n <span\n {...meterProps}\n data-og-usage-meter={primary.status}\n title={primary.resetsAt ? `${reset} ${formatAllowanceInstant(primary.resetsAt)}` : reset}\n className={cn(\"og-root inline-flex min-w-0 items-center gap-2 text-og-xs\", className)}\n >\n <span className={valueClass}>{value}</span>\n {bar}\n </span>\n );\n }\n const amounts = formatAmount\n ? labels.amounts(formatAmount(primary.used), formatAmount(primary.limit))\n : null;\n if (density === \"hero\") {\n // Lead with what's left, even at zero; the chip names the state.\n const headline = formatAmount\n ? labels.amountLeft(formatAmount(primary.remaining))\n : labels.left(usagePercentLabel(Math.max(0, 1 - primary.fraction)));\n return (\n <div\n {...meterProps}\n data-og-usage-meter={primary.status}\n className={cn(\"og-root flex min-w-0 flex-col gap-3\", className)}\n >\n {title ? <span className=\"text-og-sm font-medium text-og-fg-muted\">{title}</span> : null}\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1\">\n <span className=\"text-2xl leading-8 font-semibold tracking-[-0.5px] text-og-fg tabular-nums\">\n {headline}\n </span>\n {primary.status !== \"ok\" ? (\n <span\n className={cn(\n \"inline-flex h-[22px] items-center gap-1.5 rounded-full border px-2 text-og-xs font-medium\",\n primary.status === \"warning\"\n ? \"border-og-status-waiting/30 text-og-status-waiting\"\n : \"border-og-danger/30 text-og-danger\",\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"size-1.5 rounded-full\",\n primary.status === \"warning\" ? \"bg-og-status-waiting\" : \"bg-og-danger\",\n )}\n />\n {primary.status === \"warning\" ? labels.nearLimit : labels.limitReached}\n </span>\n ) : null}\n </div>\n {bar}\n <p className=\"text-og-xs text-og-fg-muted tabular-nums\">\n {[\n amounts ?? labels.used(usagePercentLabel(primary.fraction)),\n amounts ? `${usagePercentLabel(primary.fraction)}%` : null,\n ]\n .filter(Boolean)\n .join(\" · \")}{\" \"}\n ·{\" \"}\n <span title={primary.resetsAt ? formatAllowanceInstant(primary.resetsAt) : undefined}>\n {reset}\n </span>\n </p>\n {pool ? <PoolLine reading={pool} measure={measure} labels={labels} /> : null}\n </div>\n );\n }\n return (\n <div\n {...meterProps}\n data-og-usage-meter={primary.status}\n className={cn(\"og-root flex min-w-0 flex-col gap-2 text-og-sm\", className)}\n >\n <div className=\"flex min-w-0 items-baseline justify-between gap-3\">\n <span className=\"min-w-0 truncate font-medium text-og-fg\">{title}</span>\n <span className={valueClass}>{value}</span>\n </div>\n {bar}\n <div className=\"flex min-w-0 flex-wrap items-baseline justify-between gap-x-3 gap-y-1 text-og-xs text-og-fg-subtle\">\n <span title={primary.resetsAt ? formatAllowanceInstant(primary.resetsAt) : undefined}>\n {reset}\n </span>\n {amounts ? <span className=\"tabular-nums\">{amounts}</span> : null}\n </div>\n {pool ? <PoolLine reading={pool} measure={measure} labels={labels} /> : null}\n </div>\n );\n}\n\nfunction PoolLine({\n reading,\n measure,\n labels,\n}: {\n reading: UsageReading;\n measure: \"left\" | \"used\";\n labels: UsageMeterLabels;\n}): ReactNode {\n return (\n <p\n className={cn(\n \"text-og-xs\",\n reading.status === \"exhausted\"\n ? \"font-medium text-og-danger\"\n : reading.status === \"warning\"\n ? \"text-og-status-waiting\"\n : \"text-og-fg-muted\",\n )}\n >\n {labels.workspaceLine(valueText(reading, measure, labels))}\n </p>\n );\n}\n","import type { UsageAllowancePeriod, WorkspaceUsageResponse } from \"@opengeni/sdk/usage-allowances\";\nimport { GaugeIcon, XIcon } from \"lucide-react\";\nimport { useEffect, useState, type ReactNode } from \"react\";\n\nimport { useUsage, type UsageClientLike } from \"../hooks/use-usage\";\nimport { cn } from \"../lib/cn\";\nimport {\n allowanceLabels,\n allowanceResetSentence,\n formatAllowanceInstant,\n type AllowanceLabels,\n} from \"../usage/allowance-copy\";\nimport { summarizeUsage, usagePercentLabel, type UsageSummary } from \"../usage/summary\";\n\nexport type UsageLimitNoticeLabels = AllowanceLabels & {\n /** \"You've used 85% of your usage limit.\" */\n memberWarning: (percent: string) => string;\n /** \"This workspace has used 85% of its usage budget.\" */\n workspaceWarning: (percent: string) => string;\n memberExhausted: string;\n workspaceExhausted: string;\n dismiss: string;\n};\n\nexport const DEFAULT_USAGE_LIMIT_NOTICE_LABELS: UsageLimitNoticeLabels = {\n ...allowanceLabels(),\n memberWarning: (percent) => `You've used ${percent}% of your usage limit.`,\n workspaceWarning: (percent) => `This workspace has used ${percent}% of its usage budget.`,\n memberExhausted: \"You've reached your usage limit.\",\n workspaceExhausted: \"This workspace has reached its usage limit.\",\n dismiss: \"Dismiss\",\n};\n\ntype NoticeBaseProps = {\n labels?: Partial<UsageLimitNoticeLabels> | undefined;\n /**\n * Your own next step beside the message, for example an \"Upgrade\" or\n * \"Request more\" button. Receives the summary so it can differ by scope.\n */\n action?: ((summary: UsageSummary) => ReactNode) | undefined;\n /**\n * Remember a dismissed warning for this browser tab. The key should name the\n * workspace; the period and scope are added for you, so a new warning (or a\n * new period) shows again. Reaching the limit is never dismissible.\n */\n dismissStorageKey?: string | undefined;\n className?: string | undefined;\n};\n\nexport type UsageLimitNoticeProps = NoticeBaseProps & {\n /** A usage response you already loaded; omit it to read `/usage/me`. */\n usage?: WorkspaceUsageResponse | null | undefined;\n client?: UsageClientLike | undefined;\n workspaceId?: string | undefined;\n period?: UsageAllowancePeriod | undefined;\n refreshKey?: unknown;\n};\n\n/**\n * A calm line for the composer when a member is close to, or at, a usage\n * limit: what happened, who can raise it, and when it resets. Renders nothing\n * while usage is comfortable, unlimited, loading, or unreadable.\n */\nexport function UsageLimitNotice({\n usage,\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: UsageLimitNoticeProps) {\n if (usage !== undefined) {\n return <UsageLimitNoticeView {...view} summary={usage ? summarizeUsage(usage) : null} />;\n }\n return (\n <LoadingNotice\n {...view}\n client={client}\n workspaceId={workspaceId}\n period={period}\n refreshKey={refreshKey}\n />\n );\n}\n\nfunction LoadingNotice({\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: NoticeBaseProps &\n Pick<UsageLimitNoticeProps, \"client\" | \"workspaceId\" | \"period\" | \"refreshKey\">) {\n const { summary } = useUsage({ client, workspaceId, period, refreshKey });\n return <UsageLimitNoticeView {...view} summary={summary} />;\n}\n\nfunction readDismissed(key: string | null): boolean {\n if (!key || typeof sessionStorage === \"undefined\") return false;\n try {\n return sessionStorage.getItem(key) === \"1\";\n } catch {\n return false;\n }\n}\n\n/** Presentational notice for a summary you computed (`summarizeUsage`). */\nexport function UsageLimitNoticeView({\n summary,\n labels: overrides,\n action,\n dismissStorageKey,\n className,\n}: NoticeBaseProps & { summary: UsageSummary | null }) {\n const labels = { ...DEFAULT_USAGE_LIMIT_NOTICE_LABELS, ...overrides };\n const binding = summary?.binding ?? null;\n const state = summary?.state;\n const key =\n dismissStorageKey && binding && state === \"warning\"\n ? `${dismissStorageKey}:warning:${binding.scope}:${binding.resetsAt ?? \"none\"}`\n : null;\n const [dismissed, setDismissed] = useState(() => readDismissed(key));\n useEffect(() => setDismissed(readDismissed(key)), [key]);\n if (!summary || !binding || (state !== \"warning\" && state !== \"exhausted\")) return null;\n if (state === \"warning\" && dismissed) return null;\n const workspace = binding.scope === \"workspace\";\n const exhausted = state === \"exhausted\";\n const headline = exhausted\n ? workspace\n ? labels.workspaceExhausted\n : labels.memberExhausted\n : workspace\n ? labels.workspaceWarning(usagePercentLabel(binding.fraction))\n : labels.memberWarning(usagePercentLabel(binding.fraction));\n const reset = allowanceResetSentence(labels, binding.resetsAt);\n const dismiss = () => {\n setDismissed(true);\n if (!key) return;\n try {\n sessionStorage.setItem(key, \"1\");\n } catch {\n // Storage can be unavailable (privacy mode); the in-memory dismissal holds.\n }\n };\n return (\n <div\n role=\"status\"\n data-og-usage-notice={exhausted ? \"exhausted\" : \"warning\"}\n data-og-usage-scope={binding.scope}\n className={cn(\n \"og-root flex min-w-0 items-start gap-2 px-3.5 pt-2.5 text-og-sm md:px-4\",\n className,\n )}\n >\n <GaugeIcon aria-hidden className=\"mt-0.5 size-3.5 shrink-0 text-og-status-waiting\" />\n <p className=\"min-w-0 flex-1 text-og-fg-muted\">\n <span className={cn(exhausted && \"font-medium text-og-fg\")}>{headline}</span>{\" \"}\n {exhausted ? <>{workspace ? labels.workspaceRemedy : labels.memberRemedy} </> : null}\n <span\n className=\"whitespace-nowrap\"\n title={binding.resetsAt ? formatAllowanceInstant(binding.resetsAt) : undefined}\n >\n {reset}\n </span>\n </p>\n {action ? <div className=\"shrink-0\">{action(summary)}</div> : null}\n {!exhausted ? (\n <button\n type=\"button\"\n aria-label={labels.dismiss}\n onClick={dismiss}\n className=\"-mt-0.5 -mr-1 grid size-6 shrink-0 place-items-center rounded-og-sm text-og-fg-subtle transition hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:size-9\"\n >\n <XIcon aria-hidden className=\"size-3.5\" />\n </button>\n ) : null}\n </div>\n );\n}\n","import type {\n MemberAllowanceDefault,\n MemberAllowanceRule,\n MemberAllowanceUsage,\n WorkspaceUsageResponse,\n} from \"@opengeni/sdk/usage-allowances\";\nimport { Slider } from \"radix-ui\";\nimport { useId, useMemo, useState, type ReactNode } from \"react\";\n\nimport { cn } from \"../lib/cn\";\nimport { usagePercentLabel } from \"../usage/summary\";\n\n/** How a member is named in the list. Usage rows carry only a subject id. */\nexport type UsageMemberIdentity = {\n name: string;\n /** A second line: an email, a role, or \"You\". */\n detail?: string | null | undefined;\n};\n\n/**\n * Opt in to fixed-amount limits. Amounts are USD micros on the wire; your\n * format/parse decide the unit people type (\"$50.00\", \"500 credits\").\n */\nexport type UsageAmountFormat = {\n format: (micros: number) => string;\n /** Returns micros, or null when the text is not a valid amount. */\n parse: (text: string) => number | null;\n /** Shown before the input, for example \"$\". */\n prefix?: string | undefined;\n /** The amount input's step in the person's unit. */\n step?: number | undefined;\n};\n\nexport type UsageMemberListLabels = {\n listLabel: string;\n used: (percent: string) => string;\n noLimitUsed: string;\n amounts: (used: string, limit: string) => string;\n nearLimit: string;\n limitReached: string;\n limit: string;\n defaultEqualShare: (percent: string) => string;\n defaultNone: string;\n defaultShare: (percent: string) => string;\n defaultAmount: (amount: string) => string;\n share: (percent: string) => string;\n amount: (amount: string) => string;\n fixedAmountHidden: string;\n edit: (name: string) => string;\n editorTitle: (name: string) => string;\n optionDefault: string;\n optionShare: string;\n optionAmount: string;\n fixedAmountHint: string;\n shareSlider: string;\n shareInput: string;\n amountInput: string;\n equalShareMultiple: (multiple: string) => string;\n sameAsEqualShare: string;\n atBudget: (amount: string) => string;\n save: string;\n saving: string;\n cancel: string;\n saveFailed: string;\n invalidAmount: string;\n totalWithin: (percent: string) => string;\n totalOver: (percent: string) => string;\n oversubscribedHelp: string;\n unlimitedMembers: (count: number) => string;\n empty: string;\n};\n\nexport const DEFAULT_USAGE_MEMBER_LIST_LABELS: UsageMemberListLabels = {\n listLabel: \"Member usage\",\n used: (percent) => `${percent}% used`,\n noLimitUsed: \"No individual limit\",\n amounts: (used, limit) => `${used} of ${limit}`,\n nearLimit: \"Near limit\",\n limitReached: \"Limit reached\",\n limit: \"Limit\",\n defaultEqualShare: (percent) => `Equal share · ${percent}%`,\n defaultNone: \"No individual limit\",\n defaultShare: (percent) => `Default · ${percent}%`,\n defaultAmount: (amount) => `Default · ${amount}`,\n share: (percent) => `${percent}% of budget`,\n amount: (amount) => `${amount} fixed`,\n fixedAmountHidden: \"Fixed amount\",\n edit: (name) => `Change limit for ${name}`,\n editorTitle: (name) => `Limit for ${name}`,\n optionDefault: \"Default\",\n optionShare: \"Share of budget\",\n optionAmount: \"Fixed amount\",\n fixedAmountHint: \"Each period. Doesn't grow when the budget does.\",\n shareSlider: \"Share of the workspace budget\",\n shareInput: \"Share, percent\",\n amountInput: \"Fixed amount\",\n equalShareMultiple: (multiple) => `${multiple}× an equal share`,\n sameAsEqualShare: \"About an equal share\",\n atBudget: (amount) => `About ${amount} at the current budget`,\n save: \"Save\",\n saving: \"Saving…\",\n cancel: \"Cancel\",\n saveFailed: \"Couldn't save the limit. Refresh and try again.\",\n invalidAmount: \"Enter an amount of zero or more.\",\n totalWithin: (percent) => `Member limits add up to ${percent}% of the budget.`,\n totalOver: (percent) => `Member limits add up to ${percent}% of the budget.`,\n oversubscribedHelp:\n \"That's allowed: limits are ceilings, not reservations. The workspace budget still caps what everyone spends together.\",\n unlimitedMembers: (count) =>\n count === 1\n ? \"1 member has no individual limit.\"\n : `${count} members have no individual limit.`,\n empty: \"No members yet.\",\n};\n\nexport type UsageMemberListProps = {\n /** The full roster for one period (`getAllUsage` / every `getUsage` page). */\n usage: WorkspaceUsageResponse;\n /** The workspace's `memberDefault`, to name what \"default\" means. */\n memberDefault?: MemberAllowanceDefault | null | undefined;\n /** Name a member. Defaults to the external id, then the subject id. */\n describe?: ((member: MemberAllowanceUsage) => UsageMemberIdentity) | undefined;\n /**\n * Save a member's rule (`null` restores the default). Omit for a read-only\n * list. Run it on your backend: member limits need workspace admin authority.\n */\n onChangeRule?:\n | ((member: MemberAllowanceUsage, rule: MemberAllowanceRule) => Promise<void>)\n | undefined;\n /** Opt in to amounts and fixed-amount limits. Shares only without it. */\n amounts?: UsageAmountFormat | undefined;\n labels?: Partial<UsageMemberListLabels> | undefined;\n /** Draw the list's own border. Turn off inside a card that already has one. */\n framed?: boolean | undefined;\n className?: string | undefined;\n};\n\nconst STATUS_RANK = { exhausted: 0, warning: 1, ok: 2 } as const;\n\nfunction shareBase(usage: WorkspaceUsageResponse): number {\n return usage.workspace.includedCredits + usage.workspace.grantsRemaining;\n}\n\nfunction formatPercent(value: number): string {\n // One decimal only where a whole number would hide a real difference.\n const scaled = value * 100;\n return Number.isInteger(Math.round(scaled * 10) / 10) || scaled >= 10\n ? String(Math.round(scaled))\n : (Math.round(scaled * 10) / 10).toFixed(1);\n}\n\nfunction formatMultiple(value: number): string {\n return value >= 10 ? String(Math.round(value)) : (Math.round(value * 10) / 10).toString();\n}\n\n/** Words for a member's current limit, from their override or the default. */\nexport function describeMemberLimit(\n member: Pick<MemberAllowanceUsage, \"rule\" | \"limit\">,\n context: {\n memberDefault: MemberAllowanceDefault | null | undefined;\n shareBase: number;\n amounts?: UsageAmountFormat | undefined;\n labels?: Partial<UsageMemberListLabels> | undefined;\n },\n): string {\n const labels = { ...DEFAULT_USAGE_MEMBER_LIST_LABELS, ...context.labels };\n const ofBudget = (limit: number | null) =>\n limit !== null && context.shareBase > 0 ? formatPercent(limit / context.shareBase) : \"0\";\n const rule = member.rule;\n if (rule && \"share\" in rule) return labels.share(formatPercent(rule.share));\n if (rule && \"credits\" in rule) {\n return context.amounts\n ? labels.amount(context.amounts.format(rule.credits))\n : labels.fixedAmountHidden;\n }\n const fallback = context.memberDefault ?? \"none\";\n if (fallback === \"none\" || member.limit === null) return labels.defaultNone;\n if (fallback === \"equal_share\") return labels.defaultEqualShare(ofBudget(member.limit));\n if (\"share\" in fallback) return labels.defaultShare(formatPercent(fallback.share));\n return context.amounts\n ? labels.defaultAmount(context.amounts.format(fallback.credits))\n : labels.defaultShare(ofBudget(member.limit));\n}\n\n/**\n * Every member's usage against their own limit, with an optional editor:\n * a share-of-budget slider (default), a fixed amount, or the workspace\n * default. Oversubscription is shown, never blocked.\n */\nexport function UsageMemberList({\n usage,\n memberDefault,\n describe,\n onChangeRule,\n amounts,\n labels: overrides,\n framed = true,\n className,\n}: UsageMemberListProps) {\n const labels = { ...DEFAULT_USAGE_MEMBER_LIST_LABELS, ...overrides };\n const base = shareBase(usage);\n const [editing, setEditing] = useState<string | null>(null);\n const name = (member: MemberAllowanceUsage): UsageMemberIdentity =>\n describe?.(member) ?? {\n name: member.externalIdentity?.externalId ?? member.subjectId,\n detail: null,\n };\n const members = useMemo(\n () =>\n [...usage.members].sort(\n (left, right) =>\n STATUS_RANK[left.status] - STATUS_RANK[right.status] || right.used - left.used,\n ),\n [usage.members],\n );\n if (members.length === 0) {\n return <p className={cn(\"og-root text-og-sm text-og-fg-muted\", className)}>{labels.empty}</p>;\n }\n return (\n <div className={cn(\"og-root flex min-w-0 flex-col gap-3\", className)}>\n <AllocationSummary usage={usage} base={base} labels={labels} framed={framed} />\n <ul\n aria-label={labels.listLabel}\n className={cn(\n \"divide-y divide-og-border\",\n framed && \"overflow-hidden rounded-og-md border border-og-border\",\n )}\n >\n {members.map((member) => (\n <MemberRow\n key={member.subjectId}\n member={member}\n identity={name(member)}\n usage={usage}\n base={base}\n memberDefault={memberDefault}\n amounts={amounts}\n labels={labels}\n framed={framed}\n editing={editing === member.subjectId}\n onEdit={\n onChangeRule\n ? () =>\n setEditing((current) =>\n current === member.subjectId ? null : member.subjectId,\n )\n : undefined\n }\n onClose={() => setEditing(null)}\n onSave={\n onChangeRule\n ? async (rule) => {\n await onChangeRule(member, rule);\n setEditing(null);\n }\n : undefined\n }\n />\n ))}\n </ul>\n </div>\n );\n}\n\nfunction memberShares(usage: WorkspaceUsageResponse, base: number) {\n let total = 0;\n let unlimited = 0;\n for (const member of usage.members) {\n if (member.limit === null) unlimited += 1;\n else total += base > 0 ? member.limit / base : 0;\n }\n return { total, unlimited };\n}\n\nfunction AllocationSummary({\n usage,\n base,\n labels,\n framed,\n}: {\n usage: WorkspaceUsageResponse;\n base: number;\n labels: UsageMemberListLabels;\n framed: boolean;\n}) {\n if (base <= 0) return null;\n const { total, unlimited } = memberShares(usage, base);\n const limited = usage.members.length - unlimited;\n const over = total > 1.005;\n // An untouched default (everyone on an equal share, 100% in total) needs no\n // summary; say something only when the split has been shaped or overflows.\n const shaped = usage.members.some((member) => member.rule !== null);\n if (limited === 0 || (!over && !shaped && unlimited === 0)) return null;\n return (\n <div\n data-og-usage-allocation={over ? \"oversubscribed\" : \"within\"}\n className={cn(\n \"flex min-w-0 flex-col gap-1 text-og-sm\",\n framed || over ? \"rounded-og-md px-3 py-2\" : \"pt-1\",\n over\n ? \"bg-og-status-waiting/8 text-og-fg\"\n : framed\n ? \"bg-og-surface-1 text-og-fg-muted\"\n : \"text-og-fg-muted\",\n )}\n >\n {limited > 0 ? (\n <AllocationBar total={total} over={over}>\n {over ? labels.totalOver(formatPercent(total)) : labels.totalWithin(formatPercent(total))}\n </AllocationBar>\n ) : null}\n {over ? <p className=\"text-og-xs text-og-fg-muted\">{labels.oversubscribedHelp}</p> : null}\n {unlimited > 0 ? (\n <p className=\"text-og-xs text-og-fg-muted\">{labels.unlimitedMembers(unlimited)}</p>\n ) : null}\n </div>\n );\n}\n\nfunction AllocationBar({\n total,\n over,\n children,\n}: {\n total: number;\n over: boolean;\n children: ReactNode;\n}) {\n // The track is the budget; past 100% the fill keeps going in a second tone\n // so oversubscription reads as \"more promised than exists\", not an error.\n const scale = Math.max(1, total);\n return (\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n <p className={cn(over && \"font-medium\")}>{children}</p>\n <span aria-hidden className=\"relative block h-1 overflow-hidden rounded-full bg-og-surface-3\">\n <span\n className=\"absolute inset-y-0 left-0 bg-og-accent/70\"\n style={{ width: `${(Math.min(total, 1) / scale) * 100}%` }}\n />\n {over ? (\n <>\n <span\n className=\"absolute inset-y-0 bg-og-status-waiting\"\n style={{ left: `${(1 / scale) * 100}%`, right: 0 }}\n />\n <span\n className=\"absolute inset-y-[-2px] w-px bg-og-fg\"\n style={{ left: `${(1 / scale) * 100}%` }}\n />\n </>\n ) : null}\n </span>\n </div>\n );\n}\n\nfunction MemberRow({\n member,\n identity,\n usage,\n base,\n memberDefault,\n amounts,\n labels,\n framed,\n editing,\n onEdit,\n onClose,\n onSave,\n}: {\n framed: boolean;\n member: MemberAllowanceUsage;\n identity: UsageMemberIdentity;\n usage: WorkspaceUsageResponse;\n base: number;\n memberDefault: MemberAllowanceDefault | null | undefined;\n amounts: UsageAmountFormat | undefined;\n labels: UsageMemberListLabels;\n editing: boolean;\n onEdit: (() => void) | undefined;\n onClose: () => void;\n onSave: ((rule: MemberAllowanceRule) => Promise<void>) | undefined;\n}) {\n const editorId = useId();\n const limited = member.limit !== null;\n const fraction = member.fraction ?? 0;\n const limitText = describeMemberLimit(member, {\n memberDefault,\n shareBase: base,\n amounts,\n labels,\n });\n return (\n <li className={cn(\"min-w-0\", framed && \"bg-og-bg\")} data-og-usage-member={member.subjectId}>\n <div\n className={cn(\n \"grid min-w-0 grid-cols-1 gap-x-4 gap-y-2 py-3 sm:grid-cols-[minmax(0,1.2fr)_minmax(0,1fr)_11rem] sm:items-center\",\n framed && \"px-3.5\",\n )}\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-og-sm font-medium text-og-fg\">{identity.name}</p>\n {identity.detail ? (\n <p className=\"truncate text-og-xs text-og-fg-subtle\">{identity.detail}</p>\n ) : null}\n </div>\n <div className=\"flex min-w-0 flex-col gap-1\">\n <div className=\"flex min-w-0 items-baseline justify-between gap-2 text-og-xs\">\n <span\n className={cn(\n \"tabular-nums\",\n member.status === \"ok\" && \"text-og-fg-muted\",\n member.status === \"warning\" && \"font-medium text-og-status-waiting\",\n member.status === \"exhausted\" && \"font-medium text-og-danger\",\n )}\n >\n {limited\n ? member.status === \"exhausted\"\n ? labels.limitReached\n : `${labels.used(usagePercentLabel(fraction))}${\n member.status === \"warning\" ? ` · ${labels.nearLimit}` : \"\"\n }`\n : labels.noLimitUsed}\n </span>\n {amounts ? (\n <span className=\"truncate text-og-fg-subtle tabular-nums\">\n {limited\n ? labels.amounts(amounts.format(member.used), amounts.format(member.limit!))\n : amounts.format(member.used)}\n </span>\n ) : null}\n </div>\n <span\n aria-hidden\n className={cn(\n \"relative block h-1 overflow-hidden rounded-full\",\n limited ? \"bg-og-surface-3\" : \"bg-transparent\",\n )}\n >\n {limited ? (\n <span\n className={cn(\n \"absolute inset-y-0 left-0 rounded-full\",\n member.status === \"ok\"\n ? \"bg-og-accent\"\n : member.status === \"warning\"\n ? \"bg-og-status-waiting\"\n : \"bg-og-danger\",\n )}\n style={{ width: `${Math.min(1, Math.max(0, fraction)) * 100}%` }}\n />\n ) : null}\n </span>\n </div>\n <div className=\"flex min-w-0 items-center justify-between gap-2 sm:justify-end\">\n <span className=\"text-og-xs text-og-fg-subtle sm:hidden\">{labels.limit}</span>\n {onEdit ? (\n <button\n type=\"button\"\n onClick={onEdit}\n aria-expanded={editing}\n aria-controls={editing ? editorId : undefined}\n aria-label={`${labels.edit(identity.name)}: ${limitText}`}\n className=\"inline-flex h-8 max-w-full items-center gap-1.5 truncate rounded-og-sm border border-og-border px-2.5 text-og-control text-og-fg transition hover:bg-og-surface-2 focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:h-10\"\n >\n {limitText}\n </button>\n ) : (\n <span className=\"truncate text-og-xs text-og-fg-muted\">{limitText}</span>\n )}\n </div>\n </div>\n {editing && onSave ? (\n <MemberLimitEditor\n id={editorId}\n member={member}\n name={identity.name}\n usage={usage}\n base={base}\n memberDefault={memberDefault}\n amounts={amounts}\n labels={labels}\n framed={framed}\n onCancel={onClose}\n onSave={onSave}\n />\n ) : null}\n </li>\n );\n}\n\ntype Mode = \"default\" | \"share\" | \"amount\";\n\n/**\n * Opening the editor means \"give this person more or less\", so a member on\n * the default starts on the share slider at their current share.\n */\nfunction initialMode(rule: MemberAllowanceRule, amounts: UsageAmountFormat | undefined): Mode {\n if (rule === null) return \"share\";\n if (\"share\" in rule) return \"share\";\n return amounts ? \"amount\" : \"share\";\n}\n\nfunction MemberLimitEditor({\n id,\n member,\n name,\n usage,\n base,\n memberDefault,\n amounts,\n labels,\n framed,\n onCancel,\n onSave,\n}: {\n framed: boolean;\n id: string;\n member: MemberAllowanceUsage;\n name: string;\n usage: WorkspaceUsageResponse;\n base: number;\n memberDefault: MemberAllowanceDefault | null | undefined;\n amounts: UsageAmountFormat | undefined;\n labels: UsageMemberListLabels;\n onCancel: () => void;\n onSave: (rule: MemberAllowanceRule) => Promise<void>;\n}) {\n const count = Math.max(1, usage.members.length);\n const equal = 1 / count;\n const currentShare =\n member.rule && \"share\" in member.rule\n ? member.rule.share\n : member.limit !== null && base > 0\n ? member.limit / base\n : equal;\n const startPercent = Math.min(100, Math.max(0, Math.round(currentShare * 100)));\n const startAmount =\n amounts && member.rule && \"credits\" in member.rule\n ? stripPrefix(amounts.format(member.rule.credits), amounts.prefix)\n : amounts && member.limit !== null\n ? stripPrefix(amounts.format(member.limit), amounts.prefix)\n : \"\";\n const [mode, setMode] = useState<Mode>(() => initialMode(member.rule, amounts));\n const [sharePercent, setSharePercent] = useState(startPercent);\n const [amountText, setAmountText] = useState(startAmount);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const share = sharePercent / 100;\n const parsedAmount = amounts && mode === \"amount\" ? amounts.parse(amountText) : null;\n const defaultLimitText = describeMemberLimit(\n {\n rule: null,\n limit: member.rule === null ? member.limit : defaultLimitFor(memberDefault, base, count),\n },\n { memberDefault, shareBase: base, amounts, labels },\n );\n // Nothing to save until the person moves something: opening the slider on a\n // default member must not silently turn their default into an override.\n const unchanged =\n mode === \"default\"\n ? member.rule === null\n : mode === \"share\"\n ? (member.rule === null || \"share\" in member.rule) && sharePercent === startPercent\n : member.rule !== null && \"credits\" in member.rule && amountText === startAmount;\n // The roster total if this member's limit changes to the draft.\n const draftLimit =\n mode === \"share\"\n ? share * base\n : mode === \"amount\"\n ? parsedAmount\n : defaultLimitFor(memberDefault, base, count);\n const others = usage.members.reduce(\n (sum, other) =>\n other.subjectId === member.subjectId || other.limit === null ? sum : sum + other.limit,\n 0,\n );\n const draftTotal = base > 0 && draftLimit !== null ? (others + draftLimit) / base : null;\n\n async function save() {\n let rule: MemberAllowanceRule;\n if (mode === \"default\") rule = null;\n else if (mode === \"share\") rule = { share };\n else {\n if (parsedAmount === null || !Number.isSafeInteger(parsedAmount) || parsedAmount < 0) {\n setError(labels.invalidAmount);\n return;\n }\n rule = { credits: parsedAmount };\n }\n setSaving(true);\n setError(null);\n try {\n await onSave(rule);\n } catch (cause) {\n setError(cause instanceof Error && cause.message ? cause.message : labels.saveFailed);\n setSaving(false);\n }\n }\n\n const modes: { value: Mode; label: string }[] = [\n { value: \"share\", label: labels.optionShare },\n ...(amounts ? [{ value: \"amount\" as const, label: labels.optionAmount }] : []),\n { value: \"default\", label: labels.optionDefault },\n ];\n\n return (\n <div\n id={id}\n role=\"group\"\n aria-label={labels.editorTitle(name)}\n className={cn(\n \"bg-og-surface-1 px-3.5 py-3\",\n framed ? \"border-t border-og-border\" : \"mb-3 rounded-og-md\",\n )}\n onKeyDown={(event) => {\n if (event.key === \"Escape\" && !saving) {\n event.stopPropagation();\n onCancel();\n }\n }}\n >\n <fieldset className=\"flex min-w-0 flex-col gap-3\" disabled={saving}>\n <legend className=\"sr-only\">{labels.editorTitle(name)}</legend>\n <div\n role=\"radiogroup\"\n aria-label={labels.limit}\n className=\"inline-flex w-fit max-w-full flex-wrap gap-0.5 rounded-og-sm bg-og-surface-3/60 p-0.5\"\n >\n {modes.map((option) => (\n <label\n key={option.value}\n className={cn(\n \"relative inline-flex h-7 cursor-pointer items-center rounded-[5px] px-2.5 text-og-control transition has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-og-accent pointer-coarse:h-9\",\n mode === option.value\n ? \"bg-og-bg font-medium text-og-fg shadow-sm\"\n : \"text-og-fg-muted hover:text-og-fg\",\n )}\n >\n <input\n type=\"radio\"\n name={`${id}-mode`}\n value={option.value}\n checked={mode === option.value}\n onChange={() => setMode(option.value)}\n className=\"sr-only\"\n />\n {option.label}\n </label>\n ))}\n </div>\n\n {mode === \"share\" ? (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <Slider.Root\n value={[sharePercent]}\n min={0}\n max={100}\n step={1}\n onValueChange={(next) => setSharePercent(next[0] ?? 0)}\n data-og-share-slider=\"\"\n className=\"relative flex h-6 min-w-0 flex-1 touch-none items-center select-none pointer-coarse:h-10\"\n >\n <Slider.Track className=\"relative h-1.5 grow overflow-hidden rounded-full bg-og-surface-3\">\n <Slider.Range className=\"absolute h-full rounded-full bg-og-accent\" />\n </Slider.Track>\n {count > 1 ? (\n <span\n aria-hidden\n className=\"pointer-events-none absolute top-1/2 h-3.5 w-0.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-og-fg-subtle\"\n style={{ left: `${equal * 100}%` }}\n />\n ) : null}\n <Slider.Thumb\n aria-label={labels.shareSlider}\n aria-valuetext={`${sharePercent}%`}\n className=\"block size-4 rounded-full border-2 border-og-accent bg-og-bg shadow-sm transition focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-og-accent pointer-coarse:size-6\"\n />\n </Slider.Root>\n <label className=\"flex shrink-0 items-center gap-1 text-og-sm text-og-fg\">\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n max={100}\n step={1}\n value={sharePercent}\n aria-label={labels.shareInput}\n onChange={(event) => {\n const next = Number(event.target.value);\n if (Number.isFinite(next))\n setSharePercent(Math.min(100, Math.max(0, Math.round(next))));\n }}\n className=\"h-8 w-16 rounded-og-sm border border-og-border bg-og-bg px-2 text-right tabular-nums focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:h-10\"\n />\n %\n </label>\n </div>\n <p className=\"text-og-xs text-og-fg-subtle\" aria-live=\"polite\">\n {[\n count > 1\n ? Math.abs(share * count - 1) < 0.05\n ? labels.sameAsEqualShare\n : labels.equalShareMultiple(formatMultiple(share * count))\n : null,\n amounts && base > 0\n ? labels.atBudget(amounts.format(Math.round(share * base)))\n : null,\n ]\n .filter(Boolean)\n .join(\" · \")}\n </p>\n </div>\n ) : mode === \"amount\" && amounts ? (\n <div className=\"flex items-center gap-1 text-og-sm text-og-fg\">\n {amounts.prefix ? <span className=\"text-og-fg-subtle\">{amounts.prefix}</span> : null}\n <input\n type=\"number\"\n inputMode=\"decimal\"\n min={0}\n step={amounts.step ?? 0.01}\n value={amountText}\n aria-label={labels.amountInput}\n aria-invalid={error === labels.invalidAmount || undefined}\n onChange={(event) => setAmountText(event.target.value)}\n className=\"h-8 w-28 rounded-og-sm border border-og-border bg-og-bg px-2 tabular-nums focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:h-10\"\n />\n <span className=\"ml-2 text-og-xs text-og-fg-subtle\">{labels.fixedAmountHint}</span>\n </div>\n ) : (\n <p className=\"text-og-sm text-og-fg-muted\">{defaultLimitText}</p>\n )}\n </fieldset>\n {draftTotal !== null ? (\n <p\n className={cn(\"mt-3 text-og-xs\", draftTotal > 1.005 ? \"text-og-fg\" : \"text-og-fg-subtle\")}\n >\n {draftTotal > 1.005 ? (\n <>\n <span className=\"font-medium\">{labels.totalOver(formatPercent(draftTotal))}</span>{\" \"}\n <span className=\"text-og-fg-muted\">{labels.oversubscribedHelp}</span>\n </>\n ) : (\n labels.totalWithin(formatPercent(draftTotal))\n )}\n </p>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"mt-2 text-og-xs text-og-danger\">\n {error}\n </p>\n ) : null}\n <div className=\"mt-3 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={saving}\n className=\"inline-flex h-8 items-center rounded-og-sm px-3 text-og-control text-og-fg transition hover:bg-og-surface-2 focus-visible:outline-2 focus-visible:outline-og-accent disabled:opacity-50 pointer-coarse:h-10\"\n >\n {labels.cancel}\n </button>\n <button\n type=\"button\"\n onClick={() => void save()}\n disabled={saving || unchanged}\n className=\"inline-flex h-8 items-center rounded-og-sm border border-og-primary-border bg-og-primary px-3 text-og-control font-medium text-og-primary-fg transition hover:bg-og-primary-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:h-10\"\n >\n {saving ? labels.saving : labels.save}\n </button>\n </div>\n </div>\n );\n}\n\nfunction stripPrefix(text: string, prefix: string | undefined): string {\n const plain = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text;\n return plain.replace(/,/g, \"\");\n}\n\n/** The ceiling the workspace default gives one member (null: none). */\nfunction defaultLimitFor(\n memberDefault: MemberAllowanceDefault | null | undefined,\n base: number,\n count: number,\n): number | null {\n const fallback = memberDefault ?? \"none\";\n if (fallback === \"none\") return null;\n if (fallback === \"equal_share\") return base / count;\n if (\"share\" in fallback) return fallback.share * base;\n return fallback.credits;\n}\n"],"mappings":"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","names":["member","reading","useEffect","jsx","jsxs","useEffect","useMemo","useState","Fragment","jsx","jsxs","useState","useMemo"]}
1
+ {"version":3,"sources":["../src/hooks/use-usage.ts","../src/usage/summary.ts","../src/components/usage-meter.tsx","../src/components/usage-limit-notice.tsx","../src/components/usage-member-list.tsx"],"sourcesContent":["import type { OpenGeniClient } from \"@opengeni/sdk\";\nimport {\n getMyUsage,\n type UsageAllowancePeriod,\n type WorkspaceUsageResponse,\n} from \"@opengeni/sdk/usage-allowances\";\nimport { useCallback, useContext, useEffect, useMemo, useRef } from \"react\";\n\nimport { OpenGeniContext } from \"../session-context\";\nimport { summarizeUsage, type UsageSummary } from \"../usage/summary\";\nimport { usePolledValue } from \"./internal\";\n\n/**\n * Anything that can read the signed-in member's own usage: the root SDK client\n * pointed at your session proxy (`getMyUsage`), or any client exposing\n * `requestJson` (for example the narrower browser client).\n */\nexport type UsageClientLike =\n | Pick<OpenGeniClient, \"getMyUsage\">\n | Pick<OpenGeniClient, \"requestJson\">;\n\nexport type UseUsageOptions = {\n /** Defaults to the `<OpenGeniProvider>` client. */\n client?: UsageClientLike | undefined;\n /** Defaults to the `<OpenGeniProvider>` workspace. */\n workspaceId?: string | undefined;\n /** `\"current\"` (default) or a `YYYY-MM` allowance period. */\n period?: UsageAllowancePeriod | undefined;\n /** Re-read on an interval. Off by default; usage changes only when work settles. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n /**\n * Re-read whenever this value changes, keeping the current reading while it\n * loads. Pass something that changes when work settles (a turn count, the\n * session status) so the meter follows real usage without polling.\n */\n refreshKey?: unknown;\n};\n\nexport type UseUsageResult = {\n /** The raw `/usage/me` response: the workspace pool and your own row. */\n usage: WorkspaceUsageResponse | null;\n /** What to tell this member; null until loaded. */\n summary: UsageSummary | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nfunction readOwnUsage(\n client: UsageClientLike,\n workspaceId: string,\n period: UsageAllowancePeriod | undefined,\n signal: AbortSignal | undefined,\n): Promise<WorkspaceUsageResponse> {\n const query = period === undefined ? {} : { period };\n const options = signal ? { signal } : {};\n if (\"getMyUsage\" in client && typeof client.getMyUsage === \"function\") {\n return client.getMyUsage(workspaceId, query, options);\n }\n if (\"requestJson\" in client && typeof client.requestJson === \"function\") {\n return getMyUsage(client, workspaceId, query, options);\n }\n throw new Error(\"@opengeni/react: the usage client needs getMyUsage or requestJson.\");\n}\n\n/**\n * The signed-in member's own usage in one workspace, read through `/usage/me`\n * (the session proxy serves exactly this route). Never reads another member.\n */\nexport function useUsage(options: UseUsageOptions = {}): UseUsageResult {\n const context = useContext(OpenGeniContext);\n const client = (options.client ?? context?.client) as UsageClientLike | undefined;\n const workspaceId = options.workspaceId ?? context?.workspaceId;\n if (!client || !workspaceId) {\n throw new Error(\n \"@opengeni/react: useUsage needs a client and workspace. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId }.\",\n );\n }\n const period = options.period;\n const load = useCallback(\n (signal?: AbortSignal) => readOwnUsage(client, workspaceId, period, signal),\n [client, workspaceId, period],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const refreshKey = options.refreshKey;\n const seenKey = useRef(refreshKey);\n const refresh = state.refresh;\n useEffect(() => {\n if (Object.is(seenKey.current, refreshKey)) return;\n seenKey.current = refreshKey;\n void refresh();\n }, [refreshKey, refresh]);\n const usage = state.data;\n const summary = useMemo(() => (usage ? summarizeUsage(usage) : null), [usage]);\n return { usage, summary, loading: state.loading, error: state.error, refresh };\n}\n","import type {\n MemberAllowanceUsage,\n UsageAllowanceStatus,\n WorkspaceAllowanceUsage,\n WorkspaceUsageResponse,\n} from \"@opengeni/sdk/usage-allowances\";\n\nimport type { AllowanceScope } from \"./allowance-copy\";\n\n/** One ceiling as a person reads it: how much of it is used, and when it resets. */\nexport type UsageReading = {\n scope: AllowanceScope;\n /** Used share of the ceiling. Can exceed 1 after post-call overshoot. */\n fraction: number;\n status: UsageAllowanceStatus;\n resetsAt: string | null;\n /** USD micros. Only shown when a host opts in with `formatAmount`. */\n used: number;\n limit: number;\n remaining: number;\n};\n\n/**\n * What a member can still do: `unlimited` when no ceiling applies, otherwise\n * the state of whichever ceiling stops them first (`binding`).\n */\nexport type UsageSummary = {\n state: \"unlimited\" | \"ok\" | \"warning\" | \"exhausted\";\n /** The ceiling a person would ask to raise; null when unlimited. */\n binding: UsageReading | null;\n /** Their own ceiling, when one applies. */\n member: UsageReading | null;\n /** The shared workspace pool, when one is set. */\n workspace: UsageReading | null;\n};\n\nfunction reading(\n scope: AllowanceScope,\n usage: Pick<\n WorkspaceAllowanceUsage | MemberAllowanceUsage,\n \"limit\" | \"used\" | \"remaining\" | \"fraction\" | \"status\" | \"resetsAt\"\n >,\n): UsageReading | null {\n if (usage.limit === null) return null;\n return {\n scope,\n fraction: usage.fraction ?? (usage.limit === 0 ? 1 : usage.used / usage.limit),\n status: usage.status,\n resetsAt: usage.resetsAt,\n used: usage.used,\n limit: usage.limit,\n remaining: Math.max(0, usage.remaining ?? usage.limit - usage.used),\n };\n}\n\n/**\n * Fold a usage response (normally `/usage/me`) into what to tell one member.\n * The server's `status` decides warning/exhausted (it applies the configured\n * thresholds); when both ceilings apply, the one with less room left binds.\n */\nexport function summarizeUsage(\n usage: WorkspaceUsageResponse,\n subjectId?: string | undefined,\n): UsageSummary {\n const row =\n subjectId === undefined\n ? usage.members[0]\n : usage.members.find((member) => member.subjectId === subjectId);\n const member = row ? reading(\"member\", row) : null;\n const workspace = reading(\"workspace\", usage.workspace);\n const readings = [member, workspace].filter((value): value is UsageReading => value !== null);\n if (readings.length === 0) return { state: \"unlimited\", binding: null, member, workspace };\n // Raising a member ceiling cannot help while the shared pool is spent, so an\n // exhausted workspace binds first.\n const exhausted =\n (workspace?.status === \"exhausted\" ? workspace : null) ??\n (member?.status === \"exhausted\" ? member : null);\n if (exhausted) return { state: \"exhausted\", binding: exhausted, member, workspace };\n const warnings = readings.filter((value) => value.status === \"warning\");\n const candidates = warnings.length > 0 ? warnings : readings;\n const binding = candidates.reduce((tightest, value) =>\n value.remaining < tightest.remaining ? value : tightest,\n );\n return { state: warnings.length > 0 ? \"warning\" : \"ok\", binding, member, workspace };\n}\n\n/** A whole-number percentage for display: \"<1\" for a sliver, never \"0\" for some use. */\nexport function usagePercentLabel(fraction: number): string {\n if (!Number.isFinite(fraction) || fraction <= 0) return \"0\";\n if (fraction < 0.01) return \"<1\";\n if (fraction < 1 && Math.round(fraction * 100) === 100) return \"99\";\n return String(Math.round(fraction * 100));\n}\n","import type { UsageAllowancePeriod, WorkspaceUsageResponse } from \"@opengeni/sdk/usage-allowances\";\nimport type { ReactNode } from \"react\";\n\nimport { useUsage, type UsageClientLike } from \"../hooks/use-usage\";\nimport { cn } from \"../lib/cn\";\nimport {\n allowanceLabels,\n allowanceResetSentence,\n formatAllowanceInstant,\n type AllowanceLabels,\n} from \"../usage/allowance-copy\";\nimport {\n summarizeUsage,\n usagePercentLabel,\n type UsageReading,\n type UsageSummary,\n} from \"../usage/summary\";\n\nexport type UsageMeterLabels = AllowanceLabels & {\n /** Heading for the member's own ceiling. */\n title: string;\n /** Heading when only the shared workspace pool applies. */\n workspaceTitle: string;\n /** \"38% left\" — `percent` is preformatted (\"38\", \"<1\"). */\n left: (percent: string) => string;\n /** \"62% used\". */\n used: (percent: string) => string;\n limitReached: string;\n /** Status chip beside a hero meter that is close to its limit. */\n nearLimit: string;\n /** \"$13.00 left\" for a hero meter with amounts. */\n amountLeft: (amount: string) => string;\n /** No ceiling applies to this member. */\n unlimited: string;\n loading: string;\n error: string;\n retry: string;\n /** \"$31.00 of $50.00 used\" when the host passes `formatAmount`. */\n amounts: (used: string, limit: string) => string;\n /** The shared pool, under a member's own meter: \"Workspace: 9% left\". */\n workspaceLine: (value: string) => string;\n};\n\nexport const DEFAULT_USAGE_METER_LABELS: UsageMeterLabels = {\n ...allowanceLabels(),\n title: \"Your usage\",\n workspaceTitle: \"Workspace usage\",\n left: (percent) => `${percent}% left`,\n used: (percent) => `${percent}% used`,\n limitReached: \"Limit reached\",\n nearLimit: \"Near limit\",\n amountLeft: (amount) => `${amount} left`,\n unlimited: \"No usage limit\",\n loading: \"Checking usage…\",\n error: \"Couldn't load usage\",\n retry: \"Try again\",\n amounts: (used, limit) => `${used} of ${limit} used`,\n workspaceLine: (value) => `Workspace: ${value}`,\n};\n\nexport type UsageMeterBaseProps = {\n /** \"left\" (default) reads like a battery; \"used\" reads like spend. */\n measure?: \"left\" | \"used\" | undefined;\n /**\n * Opt in to amounts. Receives USD micros; return your own unit (\"$31.00\",\n * \"3.1 credits\", \"1.5× plan\"). Without it the meter shows only shares.\n */\n formatAmount?: ((micros: number) => string) | undefined;\n labels?: Partial<UsageMeterLabels> | undefined;\n /**\n * `compact` is one line for menus and headers; `hero` leads a page with the\n * amount (or share) left in large type. An empty `labels.title` hides the\n * heading when the page already names it.\n */\n density?: \"default\" | \"compact\" | \"hero\" | undefined;\n /** Show the shared pool under the member's own meter when it is the tighter one. Default true. */\n showWorkspace?: boolean | undefined;\n className?: string | undefined;\n};\n\nexport type UsageMeterProps = UsageMeterBaseProps & {\n /**\n * A usage response you already loaded (for example from `useUsage`).\n * Omit it to let the meter read `/usage/me` itself.\n */\n usage?: WorkspaceUsageResponse | null | undefined;\n client?: UsageClientLike | undefined;\n workspaceId?: string | undefined;\n period?: UsageAllowancePeriod | undefined;\n /** Re-read when this changes (see `useUsage`). */\n refreshKey?: unknown;\n};\n\n/**\n * The signed-in member's own usage: how much of their limit is left and when\n * it resets. Shares only by default; hosts opt in to amounts.\n */\nexport function UsageMeter({\n usage,\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: UsageMeterProps) {\n if (usage !== undefined) {\n return (\n <UsageMeterView\n {...view}\n summary={usage ? summarizeUsage(usage) : null}\n loading={usage === null}\n />\n );\n }\n return (\n <LoadingUsageMeter\n {...view}\n client={client}\n workspaceId={workspaceId}\n period={period}\n refreshKey={refreshKey}\n />\n );\n}\n\nfunction LoadingUsageMeter({\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: UsageMeterBaseProps &\n Pick<UsageMeterProps, \"client\" | \"workspaceId\" | \"period\" | \"refreshKey\">) {\n const state = useUsage({ client, workspaceId, period, refreshKey });\n return (\n <UsageMeterView\n {...view}\n summary={state.summary}\n loading={state.loading && !state.summary}\n error={state.summary ? null : state.error}\n onRetry={() => void state.refresh()}\n />\n );\n}\n\nexport type UsageMeterViewProps = UsageMeterBaseProps & {\n summary: UsageSummary | null;\n loading?: boolean | undefined;\n error?: Error | null | undefined;\n onRetry?: (() => void) | undefined;\n};\n\nconst FILL: Record<UsageReading[\"status\"], string> = {\n ok: \"bg-og-accent\",\n warning: \"bg-og-status-waiting\",\n exhausted: \"bg-og-danger\",\n};\n\nfunction valueText(\n reading: UsageReading,\n measure: \"left\" | \"used\",\n labels: UsageMeterLabels,\n): string {\n if (reading.status === \"exhausted\") return labels.limitReached;\n return measure === \"left\"\n ? labels.left(usagePercentLabel(Math.max(0, 1 - reading.fraction)))\n : labels.used(usagePercentLabel(reading.fraction));\n}\n\n/** Presentational meter for a summary you computed (`summarizeUsage`). */\nexport function UsageMeterView({\n summary,\n loading = false,\n error = null,\n onRetry,\n measure = \"left\",\n formatAmount,\n labels: overrides,\n density = \"default\",\n showWorkspace = true,\n className,\n}: UsageMeterViewProps) {\n const labels = { ...DEFAULT_USAGE_METER_LABELS, ...overrides };\n const compact = density === \"compact\";\n if (loading || error || !summary) {\n return (\n <div\n className={cn(\"og-root min-w-0 text-og-sm\", className)}\n data-og-usage-meter={error ? \"error\" : \"loading\"}\n aria-busy={loading || undefined}\n >\n {error ? (\n <p className=\"flex flex-wrap items-center gap-x-2 text-og-fg-muted\" role=\"alert\">\n {labels.error}\n {onRetry ? (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-og-sm text-og-fg underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-og-accent\"\n >\n {labels.retry}\n </button>\n ) : null}\n </p>\n ) : (\n <span className=\"text-og-fg-subtle\">{labels.loading}</span>\n )}\n </div>\n );\n }\n const primary = summary.member ?? summary.workspace;\n if (!primary) {\n return (\n <div\n className={cn(\"og-root min-w-0 text-og-sm text-og-fg-muted\", className)}\n data-og-usage-meter=\"unlimited\"\n >\n {compact ? null : <span className=\"font-medium text-og-fg\">{labels.title} · </span>}\n {labels.unlimited}\n </div>\n );\n }\n const title = summary.member ? labels.title : labels.workspaceTitle;\n const value = valueText(primary, measure, labels);\n // The bar always fills with what's used, so \"near the limit\" reads as a\n // nearly full bar whichever way the words count.\n const fill = Math.min(1, Math.max(0, primary.fraction));\n const reset = allowanceResetSentence(labels, primary.resetsAt);\n const pool =\n showWorkspace &&\n summary.member &&\n summary.workspace &&\n (summary.binding?.scope === \"workspace\" || summary.workspace.status !== \"ok\")\n ? summary.workspace\n : null;\n const meterProps = {\n role: \"meter\" as const,\n \"aria-label\": title || labels.title,\n \"aria-valuemin\": 0,\n \"aria-valuemax\": 100,\n \"aria-valuenow\": Math.round(fill * 100),\n \"aria-valuetext\": `${value}. ${reset}${\n pool ? ` ${labels.workspaceLine(valueText(pool, measure, labels))}.` : \"\"\n }`,\n };\n const bar = (\n <span\n aria-hidden\n className={cn(\n \"relative block overflow-hidden rounded-full\",\n density === \"hero\" ? \"h-1.5\" : \"h-1\",\n primary.status === \"exhausted\" ? \"bg-og-danger/15\" : \"bg-og-surface-3\",\n compact ? \"w-16 shrink-0\" : \"w-full\",\n )}\n >\n <span\n className={cn(\"absolute inset-y-0 left-0 rounded-full\", FILL[primary.status])}\n style={{ width: `${Math.round(fill * 1000) / 10}%` }}\n />\n </span>\n );\n const valueClass = cn(\n \"shrink-0 whitespace-nowrap tabular-nums\",\n primary.status === \"ok\" ? \"text-og-fg-muted\" : \"font-medium\",\n primary.status === \"warning\" && \"text-og-status-waiting\",\n primary.status === \"exhausted\" && \"text-og-danger\",\n );\n if (compact) {\n return (\n <span\n {...meterProps}\n data-og-usage-meter={primary.status}\n title={primary.resetsAt ? `${reset} ${formatAllowanceInstant(primary.resetsAt)}` : reset}\n className={cn(\"og-root inline-flex min-w-0 items-center gap-2 text-og-xs\", className)}\n >\n <span className={valueClass}>{value}</span>\n {bar}\n </span>\n );\n }\n const amounts = formatAmount\n ? labels.amounts(formatAmount(primary.used), formatAmount(primary.limit))\n : null;\n if (density === \"hero\") {\n // Lead with what's left, even at zero; the chip names the state.\n const headline = formatAmount\n ? labels.amountLeft(formatAmount(primary.remaining))\n : labels.left(usagePercentLabel(Math.max(0, 1 - primary.fraction)));\n return (\n <div\n {...meterProps}\n data-og-usage-meter={primary.status}\n className={cn(\"og-root flex min-w-0 flex-col gap-3\", className)}\n >\n {title ? <span className=\"text-og-sm font-medium text-og-fg-muted\">{title}</span> : null}\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1\">\n <span className=\"text-2xl leading-8 font-semibold tracking-[-0.5px] text-og-fg tabular-nums\">\n {headline}\n </span>\n {primary.status !== \"ok\" ? (\n <span\n className={cn(\n \"inline-flex h-[22px] items-center gap-1.5 rounded-full border px-2 text-og-xs font-medium\",\n primary.status === \"warning\"\n ? \"border-og-status-waiting/30 text-og-status-waiting\"\n : \"border-og-danger/30 text-og-danger\",\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"size-1.5 rounded-full\",\n primary.status === \"warning\" ? \"bg-og-status-waiting\" : \"bg-og-danger\",\n )}\n />\n {primary.status === \"warning\" ? labels.nearLimit : labels.limitReached}\n </span>\n ) : null}\n </div>\n {bar}\n <p className=\"text-og-xs text-og-fg-muted tabular-nums\">\n {[\n amounts ?? labels.used(usagePercentLabel(primary.fraction)),\n amounts ? `${usagePercentLabel(primary.fraction)}%` : null,\n ]\n .filter(Boolean)\n .join(\" · \")}{\" \"}\n ·{\" \"}\n <span title={primary.resetsAt ? formatAllowanceInstant(primary.resetsAt) : undefined}>\n {reset}\n </span>\n </p>\n {pool ? <PoolLine reading={pool} measure={measure} labels={labels} /> : null}\n </div>\n );\n }\n return (\n <div\n {...meterProps}\n data-og-usage-meter={primary.status}\n className={cn(\"og-root flex min-w-0 flex-col gap-2 text-og-sm\", className)}\n >\n <div className=\"flex min-w-0 items-baseline justify-between gap-3\">\n <span className=\"min-w-0 truncate font-medium text-og-fg\">{title}</span>\n <span className={valueClass}>{value}</span>\n </div>\n {bar}\n <div className=\"flex min-w-0 flex-wrap items-baseline justify-between gap-x-3 gap-y-1 text-og-xs text-og-fg-subtle\">\n <span title={primary.resetsAt ? formatAllowanceInstant(primary.resetsAt) : undefined}>\n {reset}\n </span>\n {amounts ? <span className=\"tabular-nums\">{amounts}</span> : null}\n </div>\n {pool ? <PoolLine reading={pool} measure={measure} labels={labels} /> : null}\n </div>\n );\n}\n\nfunction PoolLine({\n reading,\n measure,\n labels,\n}: {\n reading: UsageReading;\n measure: \"left\" | \"used\";\n labels: UsageMeterLabels;\n}): ReactNode {\n return (\n <p\n className={cn(\n \"text-og-xs\",\n reading.status === \"exhausted\"\n ? \"font-medium text-og-danger\"\n : reading.status === \"warning\"\n ? \"text-og-status-waiting\"\n : \"text-og-fg-muted\",\n )}\n >\n {labels.workspaceLine(valueText(reading, measure, labels))}\n </p>\n );\n}\n","import type { UsageAllowancePeriod, WorkspaceUsageResponse } from \"@opengeni/sdk/usage-allowances\";\nimport { GaugeIcon, XIcon } from \"lucide-react\";\nimport { useEffect, useState, type ReactNode } from \"react\";\n\nimport { useUsage, type UsageClientLike } from \"../hooks/use-usage\";\nimport { cn } from \"../lib/cn\";\nimport {\n allowanceLabels,\n allowanceResetSentence,\n formatAllowanceInstant,\n type AllowanceLabels,\n} from \"../usage/allowance-copy\";\nimport { summarizeUsage, usagePercentLabel, type UsageSummary } from \"../usage/summary\";\n\nexport type UsageLimitNoticeLabels = AllowanceLabels & {\n /** \"You've used 85% of your usage limit.\" */\n memberWarning: (percent: string) => string;\n /** \"This workspace has used 85% of its usage budget.\" */\n workspaceWarning: (percent: string) => string;\n memberExhausted: string;\n workspaceExhausted: string;\n dismiss: string;\n};\n\nexport const DEFAULT_USAGE_LIMIT_NOTICE_LABELS: UsageLimitNoticeLabels = {\n ...allowanceLabels(),\n memberWarning: (percent) => `You've used ${percent}% of your usage limit.`,\n workspaceWarning: (percent) => `This workspace has used ${percent}% of its usage budget.`,\n memberExhausted: \"You've reached your usage limit.\",\n workspaceExhausted: \"This workspace has reached its usage limit.\",\n dismiss: \"Dismiss\",\n};\n\ntype NoticeBaseProps = {\n labels?: Partial<UsageLimitNoticeLabels> | undefined;\n /**\n * Your own next step beside the message, for example an \"Upgrade\" or\n * \"Request more\" button. Receives the summary so it can differ by scope.\n */\n action?: ((summary: UsageSummary) => ReactNode) | undefined;\n /**\n * Remember a dismissed warning for this browser tab. The key should name the\n * workspace; the period and scope are added for you, so a new warning (or a\n * new period) shows again. Reaching the limit is never dismissible.\n */\n dismissStorageKey?: string | undefined;\n className?: string | undefined;\n};\n\nexport type UsageLimitNoticeProps = NoticeBaseProps & {\n /** A usage response you already loaded; omit it to read `/usage/me`. */\n usage?: WorkspaceUsageResponse | null | undefined;\n client?: UsageClientLike | undefined;\n workspaceId?: string | undefined;\n period?: UsageAllowancePeriod | undefined;\n refreshKey?: unknown;\n};\n\n/**\n * A calm line for the composer when a member is close to, or at, a usage\n * limit: what happened, who can raise it, and when it resets. Renders nothing\n * while usage is comfortable, unlimited, loading, or unreadable.\n */\nexport function UsageLimitNotice({\n usage,\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: UsageLimitNoticeProps) {\n if (usage !== undefined) {\n return <UsageLimitNoticeView {...view} summary={usage ? summarizeUsage(usage) : null} />;\n }\n return (\n <LoadingNotice\n {...view}\n client={client}\n workspaceId={workspaceId}\n period={period}\n refreshKey={refreshKey}\n />\n );\n}\n\nfunction LoadingNotice({\n client,\n workspaceId,\n period,\n refreshKey,\n ...view\n}: NoticeBaseProps &\n Pick<UsageLimitNoticeProps, \"client\" | \"workspaceId\" | \"period\" | \"refreshKey\">) {\n const { summary } = useUsage({ client, workspaceId, period, refreshKey });\n return <UsageLimitNoticeView {...view} summary={summary} />;\n}\n\nfunction readDismissed(key: string | null): boolean {\n if (!key || typeof sessionStorage === \"undefined\") return false;\n try {\n return sessionStorage.getItem(key) === \"1\";\n } catch {\n return false;\n }\n}\n\n/** Presentational notice for a summary you computed (`summarizeUsage`). */\nexport function UsageLimitNoticeView({\n summary,\n labels: overrides,\n action,\n dismissStorageKey,\n className,\n}: NoticeBaseProps & { summary: UsageSummary | null }) {\n const labels = { ...DEFAULT_USAGE_LIMIT_NOTICE_LABELS, ...overrides };\n const binding = summary?.binding ?? null;\n const state = summary?.state;\n const key =\n dismissStorageKey && binding && state === \"warning\"\n ? `${dismissStorageKey}:warning:${binding.scope}:${binding.resetsAt ?? \"none\"}`\n : null;\n const [dismissed, setDismissed] = useState(() => readDismissed(key));\n useEffect(() => setDismissed(readDismissed(key)), [key]);\n if (!summary || !binding || (state !== \"warning\" && state !== \"exhausted\")) return null;\n if (state === \"warning\" && dismissed) return null;\n const workspace = binding.scope === \"workspace\";\n const exhausted = state === \"exhausted\";\n const headline = exhausted\n ? workspace\n ? labels.workspaceExhausted\n : labels.memberExhausted\n : workspace\n ? labels.workspaceWarning(usagePercentLabel(binding.fraction))\n : labels.memberWarning(usagePercentLabel(binding.fraction));\n const reset = allowanceResetSentence(labels, binding.resetsAt);\n const dismiss = () => {\n setDismissed(true);\n if (!key) return;\n try {\n sessionStorage.setItem(key, \"1\");\n } catch {\n // Storage can be unavailable (privacy mode); the in-memory dismissal holds.\n }\n };\n return (\n <div\n role=\"status\"\n data-og-usage-notice={exhausted ? \"exhausted\" : \"warning\"}\n data-og-usage-scope={binding.scope}\n className={cn(\n \"og-root flex min-w-0 items-start gap-2 px-3.5 pt-2.5 text-og-sm md:px-4\",\n className,\n )}\n >\n <GaugeIcon aria-hidden className=\"mt-0.5 size-3.5 shrink-0 text-og-status-waiting\" />\n <p className=\"min-w-0 flex-1 text-og-fg-muted\">\n <span className={cn(exhausted && \"font-medium text-og-fg\")}>{headline}</span>{\" \"}\n {exhausted ? <>{workspace ? labels.workspaceRemedy : labels.memberRemedy} </> : null}\n <span\n className=\"whitespace-nowrap\"\n title={binding.resetsAt ? formatAllowanceInstant(binding.resetsAt) : undefined}\n >\n {reset}\n </span>\n </p>\n {action ? <div className=\"shrink-0\">{action(summary)}</div> : null}\n {!exhausted ? (\n <button\n type=\"button\"\n aria-label={labels.dismiss}\n onClick={dismiss}\n className=\"-mt-0.5 -mr-1 grid size-6 shrink-0 place-items-center rounded-og-sm text-og-fg-subtle transition hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:size-9\"\n >\n <XIcon aria-hidden className=\"size-3.5\" />\n </button>\n ) : null}\n </div>\n );\n}\n","import type {\n MemberAllowanceDefault,\n MemberAllowanceRule,\n MemberAllowanceUsage,\n WorkspaceUsageResponse,\n} from \"@opengeni/sdk/usage-allowances\";\nimport { Slider } from \"radix-ui\";\nimport { useId, useMemo, useState, type ReactNode } from \"react\";\n\nimport { cn } from \"../lib/cn\";\nimport { usagePercentLabel } from \"../usage/summary\";\n\n/** How a member is named in the list. Usage rows carry only a subject id. */\nexport type UsageMemberIdentity = {\n name: string;\n /** A second line: an email, a role, or \"You\". */\n detail?: string | null | undefined;\n};\n\n/**\n * Opt in to fixed-amount limits. Amounts are USD micros on the wire; your\n * format/parse decide the unit people type (\"$50.00\", \"500 credits\").\n */\nexport type UsageAmountFormat = {\n format: (micros: number) => string;\n /** Returns micros, or null when the text is not a valid amount. */\n parse: (text: string) => number | null;\n /** Shown before the input, for example \"$\". */\n prefix?: string | undefined;\n /** The amount input's step in the person's unit. */\n step?: number | undefined;\n};\n\nexport type UsageMemberListLabels = {\n listLabel: string;\n used: (percent: string) => string;\n noLimitUsed: string;\n amounts: (used: string, limit: string) => string;\n nearLimit: string;\n limitReached: string;\n limit: string;\n defaultEqualShare: (percent: string) => string;\n defaultNone: string;\n defaultShare: (percent: string) => string;\n defaultAmount: (amount: string) => string;\n share: (percent: string) => string;\n amount: (amount: string) => string;\n fixedAmountHidden: string;\n edit: (name: string) => string;\n editorTitle: (name: string) => string;\n optionDefault: string;\n optionShare: string;\n optionAmount: string;\n fixedAmountHint: string;\n shareSlider: string;\n shareInput: string;\n amountInput: string;\n equalShareMultiple: (multiple: string) => string;\n sameAsEqualShare: string;\n atBudget: (amount: string) => string;\n save: string;\n saving: string;\n cancel: string;\n saveFailed: string;\n invalidAmount: string;\n totalWithin: (percent: string) => string;\n totalOver: (percent: string) => string;\n oversubscribedHelp: string;\n unlimitedMembers: (count: number) => string;\n empty: string;\n};\n\nexport const DEFAULT_USAGE_MEMBER_LIST_LABELS: UsageMemberListLabels = {\n listLabel: \"Member usage\",\n used: (percent) => `${percent}% used`,\n noLimitUsed: \"No individual limit\",\n amounts: (used, limit) => `${used} of ${limit}`,\n nearLimit: \"Near limit\",\n limitReached: \"Limit reached\",\n limit: \"Limit\",\n defaultEqualShare: (percent) => `Equal share · ${percent}%`,\n defaultNone: \"No individual limit\",\n defaultShare: (percent) => `Default · ${percent}%`,\n defaultAmount: (amount) => `Default · ${amount}`,\n share: (percent) => `${percent}% of budget`,\n amount: (amount) => `${amount} fixed`,\n fixedAmountHidden: \"Fixed amount\",\n edit: (name) => `Change limit for ${name}`,\n editorTitle: (name) => `Limit for ${name}`,\n optionDefault: \"Default\",\n optionShare: \"Share of budget\",\n optionAmount: \"Fixed amount\",\n fixedAmountHint: \"Each period. Doesn't grow when the budget does.\",\n shareSlider: \"Share of the workspace budget\",\n shareInput: \"Share, percent\",\n amountInput: \"Fixed amount\",\n equalShareMultiple: (multiple) => `${multiple}× an equal share`,\n sameAsEqualShare: \"About an equal share\",\n atBudget: (amount) => `About ${amount} at the current budget`,\n save: \"Save\",\n saving: \"Saving…\",\n cancel: \"Cancel\",\n saveFailed: \"Couldn't save the limit. Refresh and try again.\",\n invalidAmount: \"Enter an amount of zero or more.\",\n totalWithin: (percent) => `Member limits add up to ${percent}% of the budget.`,\n totalOver: (percent) => `Member limits add up to ${percent}% of the budget.`,\n oversubscribedHelp:\n \"That's allowed: limits are ceilings, not reservations. The workspace budget still caps what everyone spends together.\",\n unlimitedMembers: (count) =>\n count === 1\n ? \"1 member has no individual limit.\"\n : `${count} members have no individual limit.`,\n empty: \"No members yet.\",\n};\n\nexport type UsageMemberListProps = {\n /** The full roster for one period (`getAllUsage` / every `getUsage` page). */\n usage: WorkspaceUsageResponse;\n /** The workspace's `memberDefault`, to name what \"default\" means. */\n memberDefault?: MemberAllowanceDefault | null | undefined;\n /** Name a member. Defaults to the external id, then the subject id. */\n describe?: ((member: MemberAllowanceUsage) => UsageMemberIdentity) | undefined;\n /**\n * Save a member's rule (`null` restores the default). Omit for a read-only\n * list. Run it on your backend: member limits need workspace admin authority.\n */\n onChangeRule?:\n | ((member: MemberAllowanceUsage, rule: MemberAllowanceRule) => Promise<void>)\n | undefined;\n /** Opt in to amounts and fixed-amount limits. Shares only without it. */\n amounts?: UsageAmountFormat | undefined;\n labels?: Partial<UsageMemberListLabels> | undefined;\n /** Draw the list's own border. Turn off inside a card that already has one. */\n framed?: boolean | undefined;\n className?: string | undefined;\n};\n\nconst STATUS_RANK = { exhausted: 0, warning: 1, ok: 2 } as const;\n\nfunction shareBase(usage: WorkspaceUsageResponse): number {\n return usage.workspace.includedCredits + usage.workspace.grantsRemaining;\n}\n\nfunction formatPercent(value: number): string {\n // One decimal only where a whole number would hide a real difference.\n const scaled = value * 100;\n return Number.isInteger(Math.round(scaled * 10) / 10) || scaled >= 10\n ? String(Math.round(scaled))\n : (Math.round(scaled * 10) / 10).toFixed(1);\n}\n\nfunction formatMultiple(value: number): string {\n return value >= 10 ? String(Math.round(value)) : (Math.round(value * 10) / 10).toString();\n}\n\n/** Words for a member's current limit, from their override or the default. */\nexport function describeMemberLimit(\n member: Pick<MemberAllowanceUsage, \"rule\" | \"limit\">,\n context: {\n memberDefault: MemberAllowanceDefault | null | undefined;\n shareBase: number;\n amounts?: UsageAmountFormat | undefined;\n labels?: Partial<UsageMemberListLabels> | undefined;\n },\n): string {\n const labels = { ...DEFAULT_USAGE_MEMBER_LIST_LABELS, ...context.labels };\n const ofBudget = (limit: number | null) =>\n limit !== null && context.shareBase > 0 ? formatPercent(limit / context.shareBase) : \"0\";\n const rule = member.rule;\n if (rule && \"share\" in rule) return labels.share(formatPercent(rule.share));\n if (rule && \"credits\" in rule) {\n return context.amounts\n ? labels.amount(context.amounts.format(rule.credits))\n : labels.fixedAmountHidden;\n }\n const fallback = context.memberDefault ?? \"none\";\n if (fallback === \"none\" || member.limit === null) return labels.defaultNone;\n if (fallback === \"equal_share\") return labels.defaultEqualShare(ofBudget(member.limit));\n if (\"share\" in fallback) return labels.defaultShare(formatPercent(fallback.share));\n return context.amounts\n ? labels.defaultAmount(context.amounts.format(fallback.credits))\n : labels.defaultShare(ofBudget(member.limit));\n}\n\n/**\n * Every member's usage against their own limit, with an optional editor:\n * a share-of-budget slider (default), a fixed amount, or the workspace\n * default. Oversubscription is shown, never blocked.\n */\nexport function UsageMemberList({\n usage,\n memberDefault,\n describe,\n onChangeRule,\n amounts,\n labels: overrides,\n framed = true,\n className,\n}: UsageMemberListProps) {\n const labels = { ...DEFAULT_USAGE_MEMBER_LIST_LABELS, ...overrides };\n const base = shareBase(usage);\n const [editing, setEditing] = useState<string | null>(null);\n const name = (member: MemberAllowanceUsage): UsageMemberIdentity =>\n describe?.(member) ?? {\n name: member.externalIdentity?.externalId ?? member.subjectId,\n detail: null,\n };\n const members = useMemo(\n () =>\n [...usage.members].sort(\n (left, right) =>\n STATUS_RANK[left.status] - STATUS_RANK[right.status] || right.used - left.used,\n ),\n [usage.members],\n );\n if (members.length === 0) {\n return <p className={cn(\"og-root text-og-sm text-og-fg-muted\", className)}>{labels.empty}</p>;\n }\n return (\n <div className={cn(\"og-root flex min-w-0 flex-col gap-3\", className)}>\n <AllocationSummary usage={usage} base={base} labels={labels} framed={framed} />\n <ul\n aria-label={labels.listLabel}\n className={cn(\n \"divide-y divide-og-border\",\n framed && \"overflow-hidden rounded-og-md border border-og-border\",\n )}\n >\n {members.map((member) => (\n <MemberRow\n key={member.subjectId}\n member={member}\n identity={name(member)}\n usage={usage}\n base={base}\n memberDefault={memberDefault}\n amounts={amounts}\n labels={labels}\n framed={framed}\n editing={editing === member.subjectId}\n onEdit={\n onChangeRule\n ? () =>\n setEditing((current) =>\n current === member.subjectId ? null : member.subjectId,\n )\n : undefined\n }\n onClose={() => setEditing(null)}\n onSave={\n onChangeRule\n ? async (rule) => {\n await onChangeRule(member, rule);\n setEditing(null);\n }\n : undefined\n }\n />\n ))}\n </ul>\n </div>\n );\n}\n\nfunction memberShares(usage: WorkspaceUsageResponse, base: number) {\n let total = 0;\n let unlimited = 0;\n for (const member of usage.members) {\n if (member.limit === null) unlimited += 1;\n else total += base > 0 ? member.limit / base : 0;\n }\n return { total, unlimited };\n}\n\nfunction AllocationSummary({\n usage,\n base,\n labels,\n framed,\n}: {\n usage: WorkspaceUsageResponse;\n base: number;\n labels: UsageMemberListLabels;\n framed: boolean;\n}) {\n if (base <= 0) return null;\n const { total, unlimited } = memberShares(usage, base);\n const limited = usage.members.length - unlimited;\n const over = total > 1.005;\n // An untouched default (everyone on an equal share, 100% in total) needs no\n // summary; say something only when the split has been shaped or overflows.\n const shaped = usage.members.some((member) => member.rule !== null);\n if (limited === 0 || (!over && !shaped && unlimited === 0)) return null;\n return (\n <div\n data-og-usage-allocation={over ? \"oversubscribed\" : \"within\"}\n className={cn(\n \"flex min-w-0 flex-col gap-1 text-og-sm\",\n framed || over ? \"rounded-og-md px-3 py-2\" : \"pt-1\",\n over\n ? \"bg-og-status-waiting/8 text-og-fg\"\n : framed\n ? \"bg-og-surface-1 text-og-fg-muted\"\n : \"text-og-fg-muted\",\n )}\n >\n {limited > 0 ? (\n <AllocationBar total={total} over={over}>\n {over ? labels.totalOver(formatPercent(total)) : labels.totalWithin(formatPercent(total))}\n </AllocationBar>\n ) : null}\n {over ? <p className=\"text-og-xs text-og-fg-muted\">{labels.oversubscribedHelp}</p> : null}\n {unlimited > 0 ? (\n <p className=\"text-og-xs text-og-fg-muted\">{labels.unlimitedMembers(unlimited)}</p>\n ) : null}\n </div>\n );\n}\n\nfunction AllocationBar({\n total,\n over,\n children,\n}: {\n total: number;\n over: boolean;\n children: ReactNode;\n}) {\n // The track is the budget; past 100% the fill keeps going in a second tone\n // so oversubscription reads as \"more promised than exists\", not an error.\n const scale = Math.max(1, total);\n return (\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n <p className={cn(over && \"font-medium\")}>{children}</p>\n <span aria-hidden className=\"relative block h-1 overflow-hidden rounded-full bg-og-surface-3\">\n <span\n className=\"absolute inset-y-0 left-0 bg-og-accent/70\"\n style={{ width: `${(Math.min(total, 1) / scale) * 100}%` }}\n />\n {over ? (\n <>\n <span\n className=\"absolute inset-y-0 bg-og-status-waiting\"\n style={{ left: `${(1 / scale) * 100}%`, right: 0 }}\n />\n <span\n className=\"absolute inset-y-[-2px] w-px bg-og-fg\"\n style={{ left: `${(1 / scale) * 100}%` }}\n />\n </>\n ) : null}\n </span>\n </div>\n );\n}\n\nfunction MemberRow({\n member,\n identity,\n usage,\n base,\n memberDefault,\n amounts,\n labels,\n framed,\n editing,\n onEdit,\n onClose,\n onSave,\n}: {\n framed: boolean;\n member: MemberAllowanceUsage;\n identity: UsageMemberIdentity;\n usage: WorkspaceUsageResponse;\n base: number;\n memberDefault: MemberAllowanceDefault | null | undefined;\n amounts: UsageAmountFormat | undefined;\n labels: UsageMemberListLabels;\n editing: boolean;\n onEdit: (() => void) | undefined;\n onClose: () => void;\n onSave: ((rule: MemberAllowanceRule) => Promise<void>) | undefined;\n}) {\n const editorId = useId();\n const limited = member.limit !== null;\n const fraction = member.fraction ?? 0;\n const limitText = describeMemberLimit(member, {\n memberDefault,\n shareBase: base,\n amounts,\n labels,\n });\n return (\n <li className={cn(\"min-w-0\", framed && \"bg-og-bg\")} data-og-usage-member={member.subjectId}>\n <div\n className={cn(\n \"grid min-w-0 grid-cols-1 gap-x-4 gap-y-2 py-3 sm:grid-cols-[minmax(0,1.2fr)_minmax(0,1fr)_11rem] sm:items-center\",\n framed && \"px-3.5\",\n )}\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-og-sm font-medium text-og-fg\">{identity.name}</p>\n {identity.detail ? (\n <p className=\"truncate text-og-xs text-og-fg-subtle\">{identity.detail}</p>\n ) : null}\n </div>\n <div className=\"flex min-w-0 flex-col gap-1\">\n <div className=\"flex min-w-0 items-baseline justify-between gap-2 text-og-xs\">\n <span\n className={cn(\n \"tabular-nums\",\n member.status === \"ok\" && \"text-og-fg-muted\",\n member.status === \"warning\" && \"font-medium text-og-status-waiting\",\n member.status === \"exhausted\" && \"font-medium text-og-danger\",\n )}\n >\n {limited\n ? member.status === \"exhausted\"\n ? labels.limitReached\n : `${labels.used(usagePercentLabel(fraction))}${\n member.status === \"warning\" ? ` · ${labels.nearLimit}` : \"\"\n }`\n : labels.noLimitUsed}\n </span>\n {amounts ? (\n <span className=\"truncate text-og-fg-subtle tabular-nums\">\n {limited\n ? labels.amounts(amounts.format(member.used), amounts.format(member.limit!))\n : amounts.format(member.used)}\n </span>\n ) : null}\n </div>\n <span\n aria-hidden\n className={cn(\n \"relative block h-1 overflow-hidden rounded-full\",\n limited ? \"bg-og-surface-3\" : \"bg-transparent\",\n )}\n >\n {limited ? (\n <span\n className={cn(\n \"absolute inset-y-0 left-0 rounded-full\",\n member.status === \"ok\"\n ? \"bg-og-accent\"\n : member.status === \"warning\"\n ? \"bg-og-status-waiting\"\n : \"bg-og-danger\",\n )}\n style={{ width: `${Math.min(1, Math.max(0, fraction)) * 100}%` }}\n />\n ) : null}\n </span>\n </div>\n <div className=\"flex min-w-0 items-center justify-between gap-2 sm:justify-end\">\n <span className=\"text-og-xs text-og-fg-subtle sm:hidden\">{labels.limit}</span>\n {onEdit ? (\n <button\n type=\"button\"\n onClick={onEdit}\n aria-expanded={editing}\n aria-controls={editing ? editorId : undefined}\n aria-label={`${labels.edit(identity.name)}: ${limitText}`}\n className=\"inline-flex h-8 max-w-full items-center gap-1.5 truncate rounded-og-sm border border-og-border px-2.5 text-og-control text-og-fg transition hover:bg-og-surface-2 focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:h-10\"\n >\n {limitText}\n </button>\n ) : (\n <span className=\"truncate text-og-xs text-og-fg-muted\">{limitText}</span>\n )}\n </div>\n </div>\n {editing && onSave ? (\n <MemberLimitEditor\n id={editorId}\n member={member}\n name={identity.name}\n usage={usage}\n base={base}\n memberDefault={memberDefault}\n amounts={amounts}\n labels={labels}\n framed={framed}\n onCancel={onClose}\n onSave={onSave}\n />\n ) : null}\n </li>\n );\n}\n\ntype Mode = \"default\" | \"share\" | \"amount\";\n\n/**\n * Opening the editor means \"give this person more or less\", so a member on\n * the default starts on the share slider at their current share.\n */\nfunction initialMode(rule: MemberAllowanceRule, amounts: UsageAmountFormat | undefined): Mode {\n if (rule === null) return \"share\";\n if (\"share\" in rule) return \"share\";\n return amounts ? \"amount\" : \"share\";\n}\n\nfunction MemberLimitEditor({\n id,\n member,\n name,\n usage,\n base,\n memberDefault,\n amounts,\n labels,\n framed,\n onCancel,\n onSave,\n}: {\n framed: boolean;\n id: string;\n member: MemberAllowanceUsage;\n name: string;\n usage: WorkspaceUsageResponse;\n base: number;\n memberDefault: MemberAllowanceDefault | null | undefined;\n amounts: UsageAmountFormat | undefined;\n labels: UsageMemberListLabels;\n onCancel: () => void;\n onSave: (rule: MemberAllowanceRule) => Promise<void>;\n}) {\n const count = Math.max(1, usage.members.length);\n const equal = 1 / count;\n const currentShare =\n member.rule && \"share\" in member.rule\n ? member.rule.share\n : member.limit !== null && base > 0\n ? member.limit / base\n : equal;\n const startPercent = Math.min(100, Math.max(0, Math.round(currentShare * 100)));\n const startAmount =\n amounts && member.rule && \"credits\" in member.rule\n ? stripPrefix(amounts.format(member.rule.credits), amounts.prefix)\n : amounts && member.limit !== null\n ? stripPrefix(amounts.format(member.limit), amounts.prefix)\n : \"\";\n const [mode, setMode] = useState<Mode>(() => initialMode(member.rule, amounts));\n const [sharePercent, setSharePercent] = useState(startPercent);\n const [amountText, setAmountText] = useState(startAmount);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const share = sharePercent / 100;\n const parsedAmount = amounts && mode === \"amount\" ? amounts.parse(amountText) : null;\n const defaultLimitText = describeMemberLimit(\n {\n rule: null,\n limit: member.rule === null ? member.limit : defaultLimitFor(memberDefault, base, count),\n },\n { memberDefault, shareBase: base, amounts, labels },\n );\n // Nothing to save until the person moves something: opening the slider on a\n // default member must not silently turn their default into an override.\n const unchanged =\n mode === \"default\"\n ? member.rule === null\n : mode === \"share\"\n ? (member.rule === null || \"share\" in member.rule) && sharePercent === startPercent\n : member.rule !== null && \"credits\" in member.rule && amountText === startAmount;\n // The roster total if this member's limit changes to the draft.\n const draftLimit =\n mode === \"share\"\n ? share * base\n : mode === \"amount\"\n ? parsedAmount\n : defaultLimitFor(memberDefault, base, count);\n const others = usage.members.reduce(\n (sum, other) =>\n other.subjectId === member.subjectId || other.limit === null ? sum : sum + other.limit,\n 0,\n );\n const draftTotal = base > 0 && draftLimit !== null ? (others + draftLimit) / base : null;\n\n async function save() {\n let rule: MemberAllowanceRule;\n if (mode === \"default\") rule = null;\n else if (mode === \"share\") rule = { share };\n else {\n if (parsedAmount === null || !Number.isSafeInteger(parsedAmount) || parsedAmount < 0) {\n setError(labels.invalidAmount);\n return;\n }\n rule = { credits: parsedAmount };\n }\n setSaving(true);\n setError(null);\n try {\n await onSave(rule);\n } catch (cause) {\n setError(cause instanceof Error && cause.message ? cause.message : labels.saveFailed);\n setSaving(false);\n }\n }\n\n const modes: { value: Mode; label: string }[] = [\n { value: \"share\", label: labels.optionShare },\n ...(amounts ? [{ value: \"amount\" as const, label: labels.optionAmount }] : []),\n { value: \"default\", label: labels.optionDefault },\n ];\n\n return (\n <div\n id={id}\n role=\"group\"\n aria-label={labels.editorTitle(name)}\n className={cn(\n \"bg-og-surface-1 px-3.5 py-3\",\n framed ? \"border-t border-og-border\" : \"mb-3 rounded-og-md\",\n )}\n onKeyDown={(event) => {\n if (event.key === \"Escape\" && !saving) {\n event.stopPropagation();\n onCancel();\n }\n }}\n >\n <fieldset className=\"flex min-w-0 flex-col gap-3\" disabled={saving}>\n <legend className=\"sr-only\">{labels.editorTitle(name)}</legend>\n <div\n role=\"radiogroup\"\n aria-label={labels.limit}\n className=\"inline-flex w-fit max-w-full flex-wrap gap-0.5 rounded-og-sm bg-og-surface-3/60 p-0.5\"\n >\n {modes.map((option) => (\n <label\n key={option.value}\n className={cn(\n \"relative inline-flex h-7 cursor-pointer items-center rounded-[5px] px-2.5 text-og-control transition has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-og-accent pointer-coarse:h-9\",\n mode === option.value\n ? \"bg-og-bg font-medium text-og-fg shadow-sm\"\n : \"text-og-fg-muted hover:text-og-fg\",\n )}\n >\n <input\n type=\"radio\"\n name={`${id}-mode`}\n value={option.value}\n checked={mode === option.value}\n onChange={() => setMode(option.value)}\n className=\"sr-only\"\n />\n {option.label}\n </label>\n ))}\n </div>\n\n {mode === \"share\" ? (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <Slider.Root\n value={[sharePercent]}\n min={0}\n max={100}\n step={1}\n onValueChange={(next) => setSharePercent(next[0] ?? 0)}\n data-og-share-slider=\"\"\n className=\"relative flex h-6 min-w-0 flex-1 touch-none items-center select-none pointer-coarse:h-10\"\n >\n <Slider.Track className=\"relative h-1.5 grow overflow-hidden rounded-full bg-og-surface-3\">\n <Slider.Range className=\"absolute h-full rounded-full bg-og-accent\" />\n </Slider.Track>\n {count > 1 ? (\n <span\n aria-hidden\n className=\"pointer-events-none absolute top-1/2 h-3.5 w-0.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-og-fg-subtle\"\n style={{ left: `${equal * 100}%` }}\n />\n ) : null}\n <Slider.Thumb\n aria-label={labels.shareSlider}\n aria-valuetext={`${sharePercent}%`}\n className=\"block size-4 rounded-full border-2 border-og-accent bg-og-bg shadow-sm transition focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-og-accent pointer-coarse:size-6\"\n />\n </Slider.Root>\n <label className=\"flex shrink-0 items-center gap-1 text-og-sm text-og-fg\">\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n max={100}\n step={1}\n value={sharePercent}\n aria-label={labels.shareInput}\n onChange={(event) => {\n const next = Number(event.target.value);\n if (Number.isFinite(next))\n setSharePercent(Math.min(100, Math.max(0, Math.round(next))));\n }}\n className=\"h-8 w-16 rounded-og-sm border border-og-border bg-og-bg px-2 text-right tabular-nums focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:h-10\"\n />\n %\n </label>\n </div>\n <p className=\"text-og-xs text-og-fg-subtle\" aria-live=\"polite\">\n {[\n count > 1\n ? Math.abs(share * count - 1) < 0.05\n ? labels.sameAsEqualShare\n : labels.equalShareMultiple(formatMultiple(share * count))\n : null,\n amounts && base > 0\n ? labels.atBudget(amounts.format(Math.round(share * base)))\n : null,\n ]\n .filter(Boolean)\n .join(\" · \")}\n </p>\n </div>\n ) : mode === \"amount\" && amounts ? (\n <div className=\"flex items-center gap-1 text-og-sm text-og-fg\">\n {amounts.prefix ? <span className=\"text-og-fg-subtle\">{amounts.prefix}</span> : null}\n <input\n type=\"number\"\n inputMode=\"decimal\"\n min={0}\n step={amounts.step ?? 0.01}\n value={amountText}\n aria-label={labels.amountInput}\n aria-invalid={error === labels.invalidAmount || undefined}\n onChange={(event) => setAmountText(event.target.value)}\n className=\"h-8 w-28 rounded-og-sm border border-og-border bg-og-bg px-2 tabular-nums focus-visible:outline-2 focus-visible:outline-og-accent pointer-coarse:h-10\"\n />\n <span className=\"ml-2 text-og-xs text-og-fg-subtle\">{labels.fixedAmountHint}</span>\n </div>\n ) : (\n <p className=\"text-og-sm text-og-fg-muted\">{defaultLimitText}</p>\n )}\n </fieldset>\n {draftTotal !== null ? (\n <p\n className={cn(\"mt-3 text-og-xs\", draftTotal > 1.005 ? \"text-og-fg\" : \"text-og-fg-subtle\")}\n >\n {draftTotal > 1.005 ? (\n <>\n <span className=\"font-medium\">{labels.totalOver(formatPercent(draftTotal))}</span>{\" \"}\n <span className=\"text-og-fg-muted\">{labels.oversubscribedHelp}</span>\n </>\n ) : (\n labels.totalWithin(formatPercent(draftTotal))\n )}\n </p>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"mt-2 text-og-xs text-og-danger\">\n {error}\n </p>\n ) : null}\n <div className=\"mt-3 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={saving}\n className=\"inline-flex h-8 items-center rounded-og-sm px-3 text-og-control text-og-fg transition hover:bg-og-surface-2 focus-visible:outline-2 focus-visible:outline-og-accent disabled:opacity-50 pointer-coarse:h-10\"\n >\n {labels.cancel}\n </button>\n <button\n type=\"button\"\n onClick={() => void save()}\n disabled={saving || unchanged}\n className=\"inline-flex h-8 items-center rounded-og-sm border border-og-primary-border bg-og-primary px-3 text-og-control font-medium text-og-primary-fg transition hover:bg-og-primary-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:h-10\"\n >\n {saving ? labels.saving : labels.save}\n </button>\n </div>\n </div>\n );\n}\n\nfunction stripPrefix(text: string, prefix: string | undefined): string {\n const plain = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text;\n return plain.replace(/,/g, \"\");\n}\n\n/** The ceiling the workspace default gives one member (null: none). */\nfunction defaultLimitFor(\n memberDefault: MemberAllowanceDefault | null | undefined,\n base: number,\n count: number,\n): number | null {\n const fallback = memberDefault ?? \"none\";\n if (fallback === \"none\") return null;\n if (fallback === \"equal_share\") return base / count;\n if (\"share\" in fallback) return fallback.share * base;\n return fallback.credits;\n}\n"],"mappings":"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","names":["member","reading","useEffect","jsx","jsxs","useEffect","useMemo","useState","Fragment","jsx","jsxs","useState","useMemo"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/react",
3
- "version": "7.6.1-canary.37073905159001",
3
+ "version": "7.6.1",
4
4
  "description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -153,8 +153,8 @@
153
153
  "@dnd-kit/utilities": "3.2.2",
154
154
  "@fontsource-variable/noto-sans-arabic": "5.2.10",
155
155
  "@fontsource-variable/noto-sans-jp": "5.2.10",
156
- "@opengeni/connect": "^0.3.1-canary.37073905159001",
157
- "@opengeni/sdk": "^7.6.1-canary.37073905159001",
156
+ "@opengeni/connect": "^0.3.1",
157
+ "@opengeni/sdk": "^7.6.1",
158
158
  "clsx": "^2.1.1",
159
159
  "dequal": "2.0.3",
160
160
  "lucide-react": "^1.8.0",
package/src/client.ts CHANGED
@@ -5,7 +5,7 @@ import type { OpenGeniClient } from "@opengeni/sdk";
5
5
  * pass the real SDK client, a proxy-backed client that routes through their
6
6
  * own API, or a scripted client in tests/demos.
7
7
  */
8
- type SessionClientMethods = Pick<
8
+ export type SessionClientLike = Pick<
9
9
  OpenGeniClient,
10
10
  // Deployment config (host-exposed models, auth, upload limits)
11
11
  | "getClientConfig"
@@ -190,10 +190,6 @@ type SessionClientMethods = Pick<
190
190
  >
191
191
  >;
192
192
 
193
- /** Compact list transport is optional for scripted and older clients. */
194
- export type SessionClientLike = SessionClientMethods &
195
- Partial<Pick<OpenGeniClient, "listSessionSummaryPage">>;
196
-
197
193
  /**
198
194
  * Tenant-safe client surface required by the session-only React entry.
199
195
  *
@@ -1,5 +1,4 @@
1
1
  import type { Permission } from "@opengeni/sdk";
2
- import { formatErrorMessage } from "@opengeni/sdk";
3
2
  import type { CommandContext, SlashArg, SlashCommand } from "./types";
4
3
 
5
4
  /**
@@ -183,10 +182,7 @@ export const defaultCommands: readonly SlashCommand[] = [
183
182
  });
184
183
  return { status: "ok", message: action === "pause" ? "Goal paused." : "Goal resumed." };
185
184
  } catch (cause) {
186
- return {
187
- status: "error",
188
- message: commandErrorMessage(ctx, cause, goalErrorMessage(cause, action)),
189
- };
185
+ return { status: "error", message: goalErrorMessage(cause, action) };
190
186
  }
191
187
  },
192
188
  },
@@ -201,7 +197,7 @@ export const defaultCommands: readonly SlashCommand[] = [
201
197
  const result = await ctx.client.compactSessionContext(ctx.workspaceId, sessionId);
202
198
  return { status: "ok", message: result.message };
203
199
  } catch (cause) {
204
- return { status: "error", message: commandErrorMessage(ctx, cause) };
200
+ return { status: "error", message: errorMessage(cause) ?? "Could not compact context." };
205
201
  }
206
202
  },
207
203
  },
@@ -223,21 +219,22 @@ export const defaultCommands: readonly SlashCommand[] = [
223
219
  await ctx.client.clearSessionContext(ctx.workspaceId, sessionId);
224
220
  return { status: "ok", message: "Context cleared." };
225
221
  } catch (cause) {
226
- return {
227
- status: "error",
228
- message: commandErrorMessage(ctx, cause, clearErrorMessage(cause)),
229
- };
222
+ return { status: "error", message: clearErrorMessage(cause) };
230
223
  }
231
224
  },
232
225
  },
233
226
  ];
234
227
 
235
- function commandErrorMessage(
236
- ctx: CommandContext,
237
- cause: unknown,
238
- message = formatErrorMessage(cause),
239
- ): string {
240
- return ctx.formatError?.(cause, message) || message;
228
+ function errorMessage(cause: unknown): string | undefined {
229
+ if (
230
+ cause &&
231
+ typeof cause === "object" &&
232
+ "message" in cause &&
233
+ typeof (cause as { message?: unknown }).message === "string"
234
+ ) {
235
+ return (cause as { message: string }).message;
236
+ }
237
+ return undefined;
241
238
  }
242
239
 
243
240
  function statusCode(cause: unknown): number | undefined {
@@ -262,12 +259,12 @@ function goalErrorMessage(cause: unknown, action: "pause" | "resume"): string {
262
259
  ? "Only a paused goal can be resumed."
263
260
  : "Goal is already in a terminal state.";
264
261
  }
265
- return formatErrorMessage(cause);
262
+ return errorMessage(cause) ?? `Could not ${action} the goal.`;
266
263
  }
267
264
 
268
265
  function clearErrorMessage(cause: unknown): string {
269
266
  if (statusCode(cause) === 409) {
270
267
  return "Pause the session and wait for the current inference to settle, then clear context.";
271
268
  }
272
- return formatErrorMessage(cause);
269
+ return errorMessage(cause) ?? "Could not clear context.";
273
270
  }
@@ -41,8 +41,6 @@ export type CommandContext = {
41
41
  permissions: Permission[];
42
42
  /** Surface a transient ok/error notice in the composer. */
43
43
  notice: (notice: Notice) => void;
44
- /** Optional presentation hook for native command failures; never rewrites command content. */
45
- formatError?: ((error: unknown, defaultMessage: string) => string) | undefined;
46
44
  /** Open the in-composer /help panel (rendered from the registry). */
47
45
  openHelp: () => void;
48
46
  /**