@spacesync/client 0.1.0 → 0.1.2

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 (122) hide show
  1. package/README.md +9 -14
  2. package/dist/analog-clock-picker.d.ts +2 -2
  3. package/dist/analog-clock-picker.js +12 -406
  4. package/dist/analog-clock-picker.js.map +1 -1
  5. package/dist/api.js +7 -87
  6. package/dist/api.js.map +1 -1
  7. package/dist/app-modal.d.ts +7 -8
  8. package/dist/app-modal.js +3 -74
  9. package/dist/app-modal.js.map +1 -1
  10. package/dist/app-shell-mobile-nav.d.ts +1 -2
  11. package/dist/app-shell-mobile-nav.js +2 -66
  12. package/dist/app-shell-mobile-nav.js.map +1 -1
  13. package/dist/app-web-shell.d.ts +2 -2
  14. package/dist/app-web-shell.js +1 -4
  15. package/dist/app-web-shell.js.map +1 -1
  16. package/dist/booking-schedule-calendars.d.ts +12 -12
  17. package/dist/booking-schedule-calendars.js +13 -402
  18. package/dist/booking-schedule-calendars.js.map +1 -1
  19. package/dist/chat-stick-to-bottom.d.ts +1 -2
  20. package/dist/chat-stick-to-bottom.js +12 -48
  21. package/dist/chat-stick-to-bottom.js.map +1 -1
  22. package/dist/chunk-4A6RCBB5.js +4925 -0
  23. package/dist/chunk-4A6RCBB5.js.map +1 -0
  24. package/dist/chunk-4NNK5IXY.js +79 -0
  25. package/dist/chunk-4NNK5IXY.js.map +1 -0
  26. package/dist/chunk-4Y5DBM6Z.js +6 -0
  27. package/dist/chunk-4Y5DBM6Z.js.map +1 -0
  28. package/dist/chunk-7YCAAP43.js +107 -0
  29. package/dist/chunk-7YCAAP43.js.map +1 -0
  30. package/dist/chunk-DMPSQ22Q.js +11 -0
  31. package/dist/chunk-DMPSQ22Q.js.map +1 -0
  32. package/dist/chunk-FCY5FYJB.js +104 -0
  33. package/dist/chunk-FCY5FYJB.js.map +1 -0
  34. package/dist/chunk-H7EX6B6N.js +580 -0
  35. package/dist/chunk-H7EX6B6N.js.map +1 -0
  36. package/dist/chunk-HCHASUB6.js +13 -0
  37. package/dist/chunk-HCHASUB6.js.map +1 -0
  38. package/dist/chunk-L65F4EMK.js +34 -0
  39. package/dist/chunk-L65F4EMK.js.map +1 -0
  40. package/dist/chunk-LUB7QWGU.js +301 -0
  41. package/dist/chunk-LUB7QWGU.js.map +1 -0
  42. package/dist/chunk-OMSJWTSZ.js +120 -0
  43. package/dist/chunk-OMSJWTSZ.js.map +1 -0
  44. package/dist/chunk-OWNUMPUC.js +9 -0
  45. package/dist/chunk-OWNUMPUC.js.map +1 -0
  46. package/dist/chunk-P65TMY3D.js +87 -0
  47. package/dist/chunk-P65TMY3D.js.map +1 -0
  48. package/dist/chunk-PBNBLCC2.js +84 -0
  49. package/dist/chunk-PBNBLCC2.js.map +1 -0
  50. package/dist/chunk-RV5PMZYZ.js +63 -0
  51. package/dist/chunk-RV5PMZYZ.js.map +1 -0
  52. package/dist/chunk-VHINHHZQ.js +48 -0
  53. package/dist/chunk-VHINHHZQ.js.map +1 -0
  54. package/dist/chunk-VVXGQKN7.js +107 -0
  55. package/dist/chunk-VVXGQKN7.js.map +1 -0
  56. package/dist/chunk-XU3EFSQX.js +22 -0
  57. package/dist/chunk-XU3EFSQX.js.map +1 -0
  58. package/dist/chunk-YAVEMXXV.js +381 -0
  59. package/dist/chunk-YAVEMXXV.js.map +1 -0
  60. package/dist/chunk-ZIJQ7BXU.js +27 -0
  61. package/dist/chunk-ZIJQ7BXU.js.map +1 -0
  62. package/dist/detail-page-header.d.ts +3 -3
  63. package/dist/detail-page-header.js +5 -149
  64. package/dist/detail-page-header.js.map +1 -1
  65. package/dist/{dialog-CIdvylPK.d.ts → dialog-CPkbi5N3.d.ts} +10 -11
  66. package/dist/file-picker-field.d.ts +2 -2
  67. package/dist/file-picker-field.js +3 -163
  68. package/dist/file-picker-field.js.map +1 -1
  69. package/dist/file-viewer-dialog.d.ts +2 -2
  70. package/dist/file-viewer-dialog.js +12 -223
  71. package/dist/file-viewer-dialog.js.map +1 -1
  72. package/dist/floor-switcher.d.ts +2 -2
  73. package/dist/floor-switcher.js +13 -323
  74. package/dist/floor-switcher.js.map +1 -1
  75. package/dist/forms.js +1 -23
  76. package/dist/forms.js.map +1 -1
  77. package/dist/image-picker-field.d.ts +7 -4
  78. package/dist/image-picker-field.js +17 -60
  79. package/dist/image-picker-field.js.map +1 -1
  80. package/dist/layout-canvas-view.d.ts +2 -2
  81. package/dist/layout-canvas-view.js +17 -220
  82. package/dist/layout-canvas-view.js.map +1 -1
  83. package/dist/lib/dateDisplay.js +1 -82
  84. package/dist/lib/dateDisplay.js.map +1 -1
  85. package/dist/live-resource-meta.js +1 -25
  86. package/dist/live-resource-meta.js.map +1 -1
  87. package/dist/live-space-pulse-dot.d.ts +2 -2
  88. package/dist/live-space-pulse-dot.js +2 -16
  89. package/dist/live-space-pulse-dot.js.map +1 -1
  90. package/dist/notification-sidebar-preview.d.ts +3 -3
  91. package/dist/notification-sidebar-preview.js +3 -61
  92. package/dist/notification-sidebar-preview.js.map +1 -1
  93. package/dist/page-list-chrome.d.ts +25 -25
  94. package/dist/page-list-chrome.js +6 -968
  95. package/dist/page-list-chrome.js.map +1 -1
  96. package/dist/platform-support-message-composer.d.ts +4 -4
  97. package/dist/platform-support-message-composer.js +15 -218
  98. package/dist/platform-support-message-composer.js.map +1 -1
  99. package/dist/shell-sidebar-nav.js +8 -13
  100. package/dist/shell-sidebar-nav.js.map +1 -1
  101. package/dist/shell-theme.js +1 -7
  102. package/dist/shell-theme.js.map +1 -1
  103. package/dist/shell-top-bar-chrome.js +3 -103
  104. package/dist/shell-top-bar-chrome.js.map +1 -1
  105. package/dist/shell-top-bar.d.ts +3 -3
  106. package/dist/shell-top-bar.js +1 -69
  107. package/dist/shell-top-bar.js.map +1 -1
  108. package/dist/support-presence-status.d.ts +2 -2
  109. package/dist/support-presence-status.js +3 -41
  110. package/dist/support-presence-status.js.map +1 -1
  111. package/dist/theme-appearance-control.d.ts +2 -2
  112. package/dist/theme-appearance-control.js +4 -63
  113. package/dist/theme-appearance-control.js.map +1 -1
  114. package/dist/theme-boot-script.js +1 -2
  115. package/dist/theme-boot-script.js.map +1 -1
  116. package/dist/theme-provider.d.ts +2 -2
  117. package/dist/theme-provider.js +2 -23
  118. package/dist/theme-provider.js.map +1 -1
  119. package/dist/ui.d.ts +261 -262
  120. package/dist/ui.js +11 -5635
  121. package/dist/ui.js.map +1 -1
  122. package/package.json +11 -2
package/dist/api.js CHANGED
@@ -1,86 +1,6 @@
1
- import { z } from 'zod';
2
- import axios2 from 'axios';
3
-
4
- // src/api/envelope.ts
5
- var paginationMetaSchema = z.object({
6
- page: z.number().optional(),
7
- limit: z.number().optional(),
8
- total: z.number().optional(),
9
- totalPages: z.number().optional()
10
- }).passthrough();
11
- var apiErrorBodySchema = z.object({
12
- success: z.literal(false),
13
- data: z.null(),
14
- error: z.object({
15
- message: z.string(),
16
- details: z.unknown().optional()
17
- }),
18
- meta: paginationMetaSchema.optional()
19
- });
20
- function apiSuccessBodySchema(dataSchema) {
21
- return z.object({
22
- success: z.literal(true),
23
- data: dataSchema,
24
- error: z.null(),
25
- meta: paginationMetaSchema.optional()
26
- });
27
- }
28
- var ApiClientError = class _ApiClientError extends Error {
29
- kind;
30
- statusCode;
31
- details;
32
- fieldErrors;
33
- constructor(kind, message, options = {}) {
34
- super(message);
35
- this.name = "ApiClientError";
36
- this.kind = kind;
37
- this.statusCode = options.statusCode;
38
- this.details = options.details;
39
- this.fieldErrors = options.fieldErrors;
40
- Object.setPrototypeOf(this, _ApiClientError.prototype);
41
- }
42
- };
43
- function isApiClientError(err) {
44
- return err instanceof ApiClientError;
45
- }
46
- function isZodIssueArray(details) {
47
- return Array.isArray(details) && details.length > 0 && details.every(
48
- (issue) => typeof issue === "object" && issue !== null && Array.isArray(issue.path) && typeof issue.message === "string"
49
- );
50
- }
51
- function toFieldErrors(issues) {
52
- return issues.map((issue) => ({
53
- path: issue.path.join("."),
54
- message: issue.message
55
- }));
56
- }
57
- function normalizeAxiosError(err) {
58
- if (isApiClientError(err)) return err;
59
- if (axios2.isAxiosError(err)) {
60
- if (!err.response) {
61
- return new ApiClientError("network", "Unable to reach the server. Check your connection and try again.");
62
- }
63
- const status = err.response.status;
64
- const parsedBody = apiErrorBodySchema.safeParse(err.response.data);
65
- const message2 = parsedBody.success ? parsedBody.data.error.message : `Request failed with status ${status}`;
66
- const details = parsedBody.success ? parsedBody.data.error.details : void 0;
67
- if (status === 400 && isZodIssueArray(details)) {
68
- return new ApiClientError("validation", message2, {
69
- statusCode: status,
70
- details,
71
- fieldErrors: toFieldErrors(details)
72
- });
73
- }
74
- return new ApiClientError("http", message2, { statusCode: status, details });
75
- }
76
- if (err instanceof z.ZodError) {
77
- return new ApiClientError("http", "Unexpected response shape from the server.", {
78
- details: err.issues
79
- });
80
- }
81
- const message = err instanceof Error ? err.message : "Something went wrong. Please try again.";
82
- return new ApiClientError("http", message);
83
- }
1
+ import { apiSuccessBodySchema, normalizeAxiosError } from './chunk-P65TMY3D.js';
2
+ export { ApiClientError, apiErrorBodySchema, apiSuccessBodySchema, isApiClientError, normalizeAxiosError, paginationMetaSchema } from './chunk-P65TMY3D.js';
3
+ import axios from 'axios';
84
4
 
85
5
  // src/api/tokenStorage.ts
86
6
  function createLocalStorageTokenStorage(namespace) {
@@ -105,12 +25,12 @@ function createLocalStorageTokenStorage(namespace) {
105
25
  }
106
26
  function createApiClient(config) {
107
27
  const { baseURL, tokenStorage, refreshPath, onAuthExpired } = config;
108
- const client = axios2.create({ baseURL });
28
+ const client = axios.create({ baseURL });
109
29
  let refreshPromise = null;
110
30
  async function refreshTokens() {
111
31
  const refreshToken = tokenStorage.getRefreshToken();
112
32
  if (!refreshToken) throw new Error("No refresh token available");
113
- const response = await axios2.post(`${baseURL}${refreshPath}`, { refreshToken });
33
+ const response = await axios.post(`${baseURL}${refreshPath}`, { refreshToken });
114
34
  const tokens = response.data.data;
115
35
  tokenStorage.setTokens(tokens);
116
36
  return tokens;
@@ -126,7 +46,7 @@ function createApiClient(config) {
126
46
  client.interceptors.response.use(
127
47
  (response) => response,
128
48
  async (error) => {
129
- if (!axios2.isAxiosError(error) || error.response?.status !== 401) {
49
+ if (!axios.isAxiosError(error) || error.response?.status !== 401) {
130
50
  throw error;
131
51
  }
132
52
  const originalRequest = error.config;
@@ -162,6 +82,6 @@ async function apiRequest(client, requestConfig, dataSchema) {
162
82
  }
163
83
  }
164
84
 
165
- export { ApiClientError, apiErrorBodySchema, apiRequest, apiSuccessBodySchema, createApiClient, createLocalStorageTokenStorage, isApiClientError, normalizeAxiosError, paginationMetaSchema };
85
+ export { apiRequest, createApiClient, createLocalStorageTokenStorage };
166
86
  //# sourceMappingURL=api.js.map
167
87
  //# sourceMappingURL=api.js.map
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/envelope.ts","../src/api/errors.ts","../src/api/tokenStorage.ts","../src/api/createApiClient.ts","../src/api/request.ts"],"names":["axios","message","z"],"mappings":"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","file":"api.js","sourcesContent":["import { z } from \"zod\";\n\n/**\n * Mirrors `apps/api/src/lib/ApiResponse.ts` exactly. Every route on the\n * backend responds with this envelope (the only exceptions are documented\n * per-service, e.g. `POST /auth/logout` returns 204 with no body).\n */\nexport interface ApiResponseMeta {\n page?: number;\n limit?: number;\n total?: number;\n totalPages?: number;\n [key: string]: unknown;\n}\n\nexport const paginationMetaSchema = z\n .object({\n page: z.number().optional(),\n limit: z.number().optional(),\n total: z.number().optional(),\n totalPages: z.number().optional(),\n })\n .passthrough();\n\nexport const apiErrorBodySchema = z.object({\n success: z.literal(false),\n data: z.null(),\n error: z.object({\n message: z.string(),\n details: z.unknown().optional(),\n }),\n meta: paginationMetaSchema.optional(),\n});\n\nexport type ApiErrorBody = z.infer<typeof apiErrorBodySchema>;\n\n/**\n * Builds a schema for the success envelope around a given data schema, e.g.\n * `apiSuccessBodySchema(bookingSchema)` or `apiSuccessBodySchema(z.array(bookingSchema))`.\n */\nexport function apiSuccessBodySchema<TData extends z.ZodTypeAny>(dataSchema: TData) {\n return z.object({\n success: z.literal(true),\n data: dataSchema,\n error: z.null(),\n meta: paginationMetaSchema.optional(),\n });\n}\n\nexport type ApiSuccessBody<TData> = {\n success: true;\n data: TData;\n error: null;\n meta?: ApiResponseMeta;\n};\n","import axios from \"axios\";\nimport { z } from \"zod\";\nimport { apiErrorBodySchema } from \"./envelope\";\n\n/**\n * - \"network\": the request never got a response at all (server unreachable, timeout, CORS/preflight failure).\n * - \"validation\": HTTP 400 from our API where `error.details` is the Zod-issues shape produced by\n * `validate.middleware.ts` — field-mappable.\n * - \"http\": every other status our API returns (401, 403, 404, 409, 500, ...). Not field-specific by default.\n */\nexport type ApiClientErrorKind = \"network\" | \"validation\" | \"http\";\n\nexport interface ApiFieldError {\n /** Dot-path, e.g. \"email\" — matched against react-hook-form field names. */\n path: string;\n message: string;\n}\n\nexport interface ApiClientErrorOptions {\n statusCode?: number;\n details?: unknown;\n fieldErrors?: ApiFieldError[];\n}\n\nexport class ApiClientError extends Error {\n readonly kind: ApiClientErrorKind;\n readonly statusCode?: number;\n readonly details?: unknown;\n readonly fieldErrors?: ApiFieldError[];\n\n constructor(kind: ApiClientErrorKind, message: string, options: ApiClientErrorOptions = {}) {\n super(message);\n this.name = \"ApiClientError\";\n this.kind = kind;\n this.statusCode = options.statusCode;\n this.details = options.details;\n this.fieldErrors = options.fieldErrors;\n Object.setPrototypeOf(this, ApiClientError.prototype);\n }\n}\n\nexport function isApiClientError(err: unknown): err is ApiClientError {\n return err instanceof ApiClientError;\n}\n\ninterface ZodLikeIssue {\n path: Array<string | number>;\n message: string;\n}\n\nfunction isZodIssueArray(details: unknown): details is ZodLikeIssue[] {\n return (\n Array.isArray(details) &&\n details.length > 0 &&\n details.every(\n (issue) =>\n typeof issue === \"object\" &&\n issue !== null &&\n Array.isArray((issue as ZodLikeIssue).path) &&\n typeof (issue as ZodLikeIssue).message === \"string\",\n )\n );\n}\n\nfunction toFieldErrors(issues: ZodLikeIssue[]): ApiFieldError[] {\n return issues.map((issue) => ({\n path: issue.path.join(\".\"),\n message: issue.message,\n }));\n}\n\n/**\n * Normalizes anything that can be thrown while making a request (axios error,\n * envelope-shape mismatch, or an unexpected exception) into one `ApiClientError`,\n * classified by `kind` so callers like `useApiForm` can apply a sensible default\n * per failure mode instead of treating a dropped connection the same as a 409.\n */\nexport function normalizeAxiosError(err: unknown): ApiClientError {\n if (isApiClientError(err)) return err;\n\n if (axios.isAxiosError(err)) {\n if (!err.response) {\n return new ApiClientError(\"network\", \"Unable to reach the server. Check your connection and try again.\");\n }\n\n const status = err.response.status;\n const parsedBody = apiErrorBodySchema.safeParse(err.response.data);\n const message = parsedBody.success ? parsedBody.data.error.message : `Request failed with status ${status}`;\n const details = parsedBody.success ? parsedBody.data.error.details : undefined;\n\n if (status === 400 && isZodIssueArray(details)) {\n return new ApiClientError(\"validation\", message, {\n statusCode: status,\n details,\n fieldErrors: toFieldErrors(details),\n });\n }\n\n return new ApiClientError(\"http\", message, { statusCode: status, details });\n }\n\n if (err instanceof z.ZodError) {\n // The server responded 2xx but the payload didn't match the schema we expected -\n // a frontend/backend contract mismatch, not a user-facing form error.\n return new ApiClientError(\"http\", \"Unexpected response shape from the server.\", {\n details: err.issues,\n });\n }\n\n const message = err instanceof Error ? err.message : \"Something went wrong. Please try again.\";\n return new ApiClientError(\"http\", message);\n}\n","export interface AuthTokens {\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface TokenStorage {\n getAccessToken(): string | null;\n getRefreshToken(): string | null;\n setTokens(tokens: AuthTokens): void;\n clearTokens(): void;\n}\n\n/**\n * localStorage-backed token storage, namespaced per app/scope (e.g. \"tenant\", \"platform\")\n * so multiple apps on the same origin (or a single app talking to two backends) never\n * collide on the same keys.\n */\nexport function createLocalStorageTokenStorage(namespace: string): TokenStorage {\n const accessKey = `coworking.${namespace}.accessToken`;\n const refreshKey = `coworking.${namespace}.refreshToken`;\n\n return {\n getAccessToken() {\n return localStorage.getItem(accessKey);\n },\n getRefreshToken() {\n return localStorage.getItem(refreshKey);\n },\n setTokens({ accessToken, refreshToken }) {\n localStorage.setItem(accessKey, accessToken);\n localStorage.setItem(refreshKey, refreshToken);\n },\n clearTokens() {\n localStorage.removeItem(accessKey);\n localStorage.removeItem(refreshKey);\n },\n };\n}\n","import axios, { type AxiosInstance } from \"axios\";\nimport type { AuthTokens, TokenStorage } from \"./tokenStorage\";\n\ndeclare module \"axios\" {\n export interface AxiosRequestConfig {\n /** Set on requests (login/register/refresh) that should never get a Bearer header or trigger a refresh-retry. */\n skipAuth?: boolean;\n /** Internal flag set by the response interceptor to avoid retrying the same request twice. */\n _retriedAfterRefresh?: boolean;\n }\n}\n\nexport interface CreateApiClientConfig {\n baseURL: string;\n tokenStorage: TokenStorage;\n /** Path (relative to `baseURL`) used to exchange a refresh token for a new token pair, e.g. \"/auth/refresh\". */\n refreshPath: string;\n /** Called once refreshing the session has failed (bad/expired refresh token) - e.g. navigate to /login. */\n onAuthExpired?: () => void;\n}\n\n/**\n * Creates an axios instance that:\n * - attaches `Authorization: Bearer <accessToken>` to every request (unless `skipAuth` is set on that request's config)\n * - on a 401, refreshes the session exactly once (concurrent 401s share the same in-flight refresh call via\n * `refreshPromise`, so a burst of requests never fires multiple refreshes) and retries the original request\n * - on refresh failure, clears stored tokens and calls `onAuthExpired` so the app can redirect to login\n *\n * Does NOT unwrap the `{ success, data, error }` envelope itself - see `request.ts` for that - so raw responses\n * (e.g. the 204 from `/auth/logout`) are still inspectable when needed.\n */\nexport function createApiClient(config: CreateApiClientConfig): AxiosInstance {\n const { baseURL, tokenStorage, refreshPath, onAuthExpired } = config;\n const client = axios.create({ baseURL });\n\n let refreshPromise: Promise<AuthTokens> | null = null;\n\n async function refreshTokens(): Promise<AuthTokens> {\n const refreshToken = tokenStorage.getRefreshToken();\n if (!refreshToken) throw new Error(\"No refresh token available\");\n\n // Uses the bare `axios` instance (not `client`) so this call never passes through the\n // interceptors below - it can't trigger its own refresh-retry loop.\n const response = await axios.post<{ data: AuthTokens }>(`${baseURL}${refreshPath}`, { refreshToken });\n const tokens = response.data.data;\n tokenStorage.setTokens(tokens);\n return tokens;\n }\n\n client.interceptors.request.use((requestConfig) => {\n if (requestConfig.skipAuth) return requestConfig;\n\n const accessToken = tokenStorage.getAccessToken();\n if (accessToken) {\n requestConfig.headers.set(\"Authorization\", `Bearer ${accessToken}`);\n }\n return requestConfig;\n });\n\n client.interceptors.response.use(\n (response) => response,\n async (error) => {\n if (!axios.isAxiosError(error) || error.response?.status !== 401) {\n throw error;\n }\n\n const originalRequest = error.config;\n if (!originalRequest || originalRequest.skipAuth || originalRequest._retriedAfterRefresh) {\n throw error;\n }\n\n try {\n refreshPromise ??= refreshTokens().finally(() => {\n refreshPromise = null;\n });\n const tokens = await refreshPromise;\n\n originalRequest._retriedAfterRefresh = true;\n originalRequest.headers.set(\"Authorization\", `Bearer ${tokens.accessToken}`);\n return client.request(originalRequest);\n } catch {\n tokenStorage.clearTokens();\n onAuthExpired?.();\n throw error; // surface the original 401, not the refresh failure\n }\n },\n );\n\n return client;\n}\n","import type { AxiosInstance, AxiosRequestConfig } from \"axios\";\nimport type { z } from \"zod\";\nimport { apiSuccessBodySchema, type ApiResponseMeta } from \"./envelope\";\nimport { normalizeAxiosError } from \"./errors\";\n\nexport interface ApiRequestResult<TData> {\n data: TData;\n meta?: ApiResponseMeta;\n}\n\n/**\n * Runs a request through the given client, validates + unwraps the `{ success, data, error, meta }`\n * envelope against `dataSchema`, and normalizes any failure (network, envelope-shape mismatch, or a\n * `{ success: false }` response) into an `ApiClientError`.\n *\n * This is the one place response validation + error normalization happens, so every service call\n * built on top of it gets a runtime-verified response \"for free\" - if the backend's actual response\n * doesn't match `dataSchema`, this throws instead of silently returning malformed data.\n */\nexport async function apiRequest<TData>(\n client: AxiosInstance,\n requestConfig: AxiosRequestConfig,\n dataSchema: z.ZodType<TData>,\n): Promise<ApiRequestResult<TData>> {\n try {\n const response = await client.request(requestConfig);\n const parsed = apiSuccessBodySchema(dataSchema).parse(response.data);\n // `parsed.data` is structurally `TData` by construction (it came from `dataSchema`), but TS\n // can't prove that through a generic zod composition - narrow it explicitly.\n return { data: parsed.data as TData, meta: parsed.meta };\n } catch (err) {\n throw normalizeAxiosError(err);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/api/tokenStorage.ts","../src/api/createApiClient.ts","../src/api/request.ts"],"names":[],"mappings":"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","file":"api.js","sourcesContent":["export interface AuthTokens {\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface TokenStorage {\n getAccessToken(): string | null;\n getRefreshToken(): string | null;\n setTokens(tokens: AuthTokens): void;\n clearTokens(): void;\n}\n\n/**\n * localStorage-backed token storage, namespaced per app/scope (e.g. \"tenant\", \"platform\")\n * so multiple apps on the same origin (or a single app talking to two backends) never\n * collide on the same keys.\n */\nexport function createLocalStorageTokenStorage(namespace: string): TokenStorage {\n const accessKey = `coworking.${namespace}.accessToken`;\n const refreshKey = `coworking.${namespace}.refreshToken`;\n\n return {\n getAccessToken() {\n return localStorage.getItem(accessKey);\n },\n getRefreshToken() {\n return localStorage.getItem(refreshKey);\n },\n setTokens({ accessToken, refreshToken }) {\n localStorage.setItem(accessKey, accessToken);\n localStorage.setItem(refreshKey, refreshToken);\n },\n clearTokens() {\n localStorage.removeItem(accessKey);\n localStorage.removeItem(refreshKey);\n },\n };\n}\n","import axios, { type AxiosInstance } from \"axios\";\nimport type { AuthTokens, TokenStorage } from \"./tokenStorage\";\n\ndeclare module \"axios\" {\n export interface AxiosRequestConfig {\n /** Set on requests (login/register/refresh) that should never get a Bearer header or trigger a refresh-retry. */\n skipAuth?: boolean;\n /** Internal flag set by the response interceptor to avoid retrying the same request twice. */\n _retriedAfterRefresh?: boolean;\n }\n}\n\nexport interface CreateApiClientConfig {\n baseURL: string;\n tokenStorage: TokenStorage;\n /** Path (relative to `baseURL`) used to exchange a refresh token for a new token pair, e.g. \"/auth/refresh\". */\n refreshPath: string;\n /** Called once refreshing the session has failed (bad/expired refresh token) - e.g. navigate to /login. */\n onAuthExpired?: () => void;\n}\n\n/**\n * Creates an axios instance that:\n * - attaches `Authorization: Bearer <accessToken>` to every request (unless `skipAuth` is set on that request's config)\n * - on a 401, refreshes the session exactly once (concurrent 401s share the same in-flight refresh call via\n * `refreshPromise`, so a burst of requests never fires multiple refreshes) and retries the original request\n * - on refresh failure, clears stored tokens and calls `onAuthExpired` so the app can redirect to login\n *\n * Does NOT unwrap the `{ success, data, error }` envelope itself - see `request.ts` for that - so raw responses\n * (e.g. the 204 from `/auth/logout`) are still inspectable when needed.\n */\nexport function createApiClient(config: CreateApiClientConfig): AxiosInstance {\n const { baseURL, tokenStorage, refreshPath, onAuthExpired } = config;\n const client = axios.create({ baseURL });\n\n let refreshPromise: Promise<AuthTokens> | null = null;\n\n async function refreshTokens(): Promise<AuthTokens> {\n const refreshToken = tokenStorage.getRefreshToken();\n if (!refreshToken) throw new Error(\"No refresh token available\");\n\n // Uses the bare `axios` instance (not `client`) so this call never passes through the\n // interceptors below - it can't trigger its own refresh-retry loop.\n const response = await axios.post<{ data: AuthTokens }>(`${baseURL}${refreshPath}`, { refreshToken });\n const tokens = response.data.data;\n tokenStorage.setTokens(tokens);\n return tokens;\n }\n\n client.interceptors.request.use((requestConfig) => {\n if (requestConfig.skipAuth) return requestConfig;\n\n const accessToken = tokenStorage.getAccessToken();\n if (accessToken) {\n requestConfig.headers.set(\"Authorization\", `Bearer ${accessToken}`);\n }\n return requestConfig;\n });\n\n client.interceptors.response.use(\n (response) => response,\n async (error) => {\n if (!axios.isAxiosError(error) || error.response?.status !== 401) {\n throw error;\n }\n\n const originalRequest = error.config;\n if (!originalRequest || originalRequest.skipAuth || originalRequest._retriedAfterRefresh) {\n throw error;\n }\n\n try {\n refreshPromise ??= refreshTokens().finally(() => {\n refreshPromise = null;\n });\n const tokens = await refreshPromise;\n\n originalRequest._retriedAfterRefresh = true;\n originalRequest.headers.set(\"Authorization\", `Bearer ${tokens.accessToken}`);\n return client.request(originalRequest);\n } catch {\n tokenStorage.clearTokens();\n onAuthExpired?.();\n throw error; // surface the original 401, not the refresh failure\n }\n },\n );\n\n return client;\n}\n","import type { AxiosInstance, AxiosRequestConfig } from \"axios\";\nimport type { z } from \"zod\";\nimport { apiSuccessBodySchema, type ApiResponseMeta } from \"./envelope\";\nimport { normalizeAxiosError } from \"./errors\";\n\nexport interface ApiRequestResult<TData> {\n data: TData;\n meta?: ApiResponseMeta;\n}\n\n/**\n * Runs a request through the given client, validates + unwraps the `{ success, data, error, meta }`\n * envelope against `dataSchema`, and normalizes any failure (network, envelope-shape mismatch, or a\n * `{ success: false }` response) into an `ApiClientError`.\n *\n * This is the one place response validation + error normalization happens, so every service call\n * built on top of it gets a runtime-verified response \"for free\" - if the backend's actual response\n * doesn't match `dataSchema`, this throws instead of silently returning malformed data.\n */\nexport async function apiRequest<TData>(\n client: AxiosInstance,\n requestConfig: AxiosRequestConfig,\n dataSchema: z.ZodType<TData>,\n): Promise<ApiRequestResult<TData>> {\n try {\n const response = await client.request(requestConfig);\n const parsed = apiSuccessBodySchema(dataSchema).parse(response.data);\n // `parsed.data` is structurally `TData` by construction (it came from `dataSchema`), but TS\n // can't prove that through a generic zod composition - narrow it explicitly.\n return { data: parsed.data as TData, meta: parsed.meta };\n } catch (err) {\n throw normalizeAxiosError(err);\n }\n}\n"]}
@@ -1,25 +1,24 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import * as React from 'react';
3
- import { D as DialogContent, a as DialogFooter, b as DialogHeader } from './dialog-CIdvylPK.js';
2
+ import { D as DialogContent, a as DialogFooter, b as DialogHeader } from './dialog-CPkbi5N3.js';
4
3
  import '@radix-ui/react-dialog';
5
4
 
6
5
  /** Shared shell for `DialogContent` when header/body/footer supply their own padding (`p-0`). */
7
6
  declare const appModalDialogContentChrome = "bg-card border-border rounded-xl text-foreground shadow-xl gap-0 p-0 overflow-hidden";
8
- declare function AppModalDialogContent({ className, ...props }: React.ComponentProps<typeof DialogContent>): react_jsx_runtime.JSX.Element;
9
- declare function AppModalDialogHeader({ className, ...props }: React.ComponentProps<typeof DialogHeader>): react_jsx_runtime.JSX.Element;
10
- declare function AppModalDialogBody({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
7
+ declare function AppModalDialogContent({ className, ...props }: React.ComponentProps<typeof DialogContent>): React.JSX.Element;
8
+ declare function AppModalDialogHeader({ className, ...props }: React.ComponentProps<typeof DialogHeader>): React.JSX.Element;
9
+ declare function AppModalDialogBody({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
11
10
  declare function AppModalDialogFooter({ className, muted, ...props }: React.ComponentProps<typeof DialogFooter> & {
12
11
  muted?: boolean;
13
- }): react_jsx_runtime.JSX.Element;
12
+ }): React.JSX.Element;
14
13
  type ChromeHeaderVariant = "row" | "stack";
15
14
  /** Header region for Radix `Dialog.Content` layouts (custom close in header). */
16
15
  declare function AppModalChromeHeader({ variant, className, ...props }: React.ComponentProps<"div"> & {
17
16
  variant?: ChromeHeaderVariant;
18
- }): react_jsx_runtime.JSX.Element;
17
+ }): React.JSX.Element;
19
18
  type ChromeFooterVariant = "end" | "split";
20
19
  /** Footer region for Radix `Dialog.Content` layouts. */
21
20
  declare function AppModalChromeFooter({ variant, className, ...props }: React.ComponentProps<"div"> & {
22
21
  variant?: ChromeFooterVariant;
23
- }): react_jsx_runtime.JSX.Element;
22
+ }): React.JSX.Element;
24
23
 
25
24
  export { AppModalChromeFooter, AppModalChromeHeader, AppModalDialogBody, AppModalDialogContent, AppModalDialogFooter, AppModalDialogHeader, appModalDialogContentChrome };
package/dist/app-modal.js CHANGED
@@ -1,78 +1,7 @@
1
- import * as DialogPrimitive from '@radix-ui/react-dialog';
2
- import { XIcon } from 'lucide-react';
3
- import { clsx } from 'clsx';
4
- import { twMerge } from 'tailwind-merge';
5
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { DialogContent, DialogHeader, DialogFooter } from './chunk-7YCAAP43.js';
2
+ import { cn } from './chunk-DMPSQ22Q.js';
3
+ import { jsx } from 'react/jsx-runtime';
6
4
 
7
- function cn(...inputs) {
8
- return twMerge(clsx(inputs));
9
- }
10
- function DialogPortal({ ...props }) {
11
- return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
12
- }
13
- function DialogOverlay({
14
- className,
15
- ...props
16
- }) {
17
- return /* @__PURE__ */ jsx(
18
- DialogPrimitive.Overlay,
19
- {
20
- "data-slot": "dialog-overlay",
21
- className: cn(
22
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
23
- className
24
- ),
25
- ...props
26
- }
27
- );
28
- }
29
- function DialogContent({
30
- className,
31
- children,
32
- ...props
33
- }) {
34
- return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
35
- /* @__PURE__ */ jsx(DialogOverlay, {}),
36
- /* @__PURE__ */ jsxs(
37
- DialogPrimitive.Content,
38
- {
39
- "data-slot": "dialog-content",
40
- className: cn(
41
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
42
- className
43
- ),
44
- ...props,
45
- children: [
46
- children,
47
- /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [
48
- /* @__PURE__ */ jsx(XIcon, {}),
49
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
50
- ] })
51
- ]
52
- }
53
- )
54
- ] });
55
- }
56
- function DialogHeader({ className, ...props }) {
57
- return /* @__PURE__ */ jsx(
58
- "div",
59
- {
60
- "data-slot": "dialog-header",
61
- className: cn("flex flex-col gap-2 text-center sm:text-left", className),
62
- ...props
63
- }
64
- );
65
- }
66
- function DialogFooter({ className, ...props }) {
67
- return /* @__PURE__ */ jsx(
68
- "div",
69
- {
70
- "data-slot": "dialog-footer",
71
- className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
72
- ...props
73
- }
74
- );
75
- }
76
5
  var appModalDialogContentChrome = "bg-card border-border rounded-xl text-foreground shadow-xl gap-0 p-0 overflow-hidden";
77
6
  function AppModalDialogContent({
78
7
  className,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ui/utils.ts","../src/ui/dialog.tsx","../src/shell/app-modal.tsx"],"names":["jsx"],"mappings":"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","file":"app-modal.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } from \"lucide-react\";\n\nimport { cn } from \"./utils\";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content>) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg\",\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\">\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n","\"use client\";\n\nimport * as React from \"react\";\nimport { DialogContent, DialogFooter, DialogHeader } from \"../ui/dialog\";\nimport { cn } from \"../ui/utils\";\n\n/** Shared shell for `DialogContent` when header/body/footer supply their own padding (`p-0`). */\nexport const appModalDialogContentChrome =\n \"bg-card border-border rounded-xl text-foreground shadow-xl gap-0 p-0 overflow-hidden\";\n\nexport function AppModalDialogContent({\n className,\n ...props\n}: React.ComponentProps<typeof DialogContent>) {\n return <DialogContent className={cn(appModalDialogContentChrome, className)} {...props} />;\n}\n\nexport function AppModalDialogHeader({\n className,\n ...props\n}: React.ComponentProps<typeof DialogHeader>) {\n return <DialogHeader className={cn(\"px-6 pt-6 pb-4\", className)} {...props} />;\n}\n\nexport function AppModalDialogBody({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return <div className={cn(\"px-6 pb-4 flex flex-col gap-4\", className)} {...props} />;\n}\n\nexport function AppModalDialogFooter({\n className,\n muted = false,\n ...props\n}: React.ComponentProps<typeof DialogFooter> & { muted?: boolean }) {\n return (\n <DialogFooter\n className={cn(\"px-6 py-4 gap-2 sm:gap-2\", muted && \"bg-muted/80\", className)}\n {...props}\n />\n );\n}\n\ntype ChromeHeaderVariant = \"row\" | \"stack\";\n\n/** Header region for Radix `Dialog.Content` layouts (custom close in header). */\nexport function AppModalChromeHeader({\n variant = \"row\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { variant?: ChromeHeaderVariant }) {\n return (\n <div\n className={cn(\n \"shrink-0\",\n variant === \"stack\"\n ? \"flex items-start justify-between gap-4 px-6 py-4\"\n : \"flex items-center justify-between px-6 pt-5 pb-4\",\n className,\n )}\n {...props}\n />\n );\n}\n\ntype ChromeFooterVariant = \"end\" | \"split\";\n\n/** Footer region for Radix `Dialog.Content` layouts. */\nexport function AppModalChromeFooter({\n variant = \"end\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { variant?: ChromeFooterVariant }) {\n return (\n <div\n className={cn(\n \"shrink-0\",\n variant === \"split\"\n ? \"flex items-center justify-between gap-2 px-6 py-4 bg-card\"\n : \"flex items-center justify-end gap-2 px-6 py-4\",\n className,\n )}\n {...props}\n />\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/shell/app-modal.tsx"],"names":[],"mappings":";;;;AAOO,IAAM,2BAAA,GACX;AAEK,SAAS,qBAAA,CAAsB;AAAA,EACpC,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA+C;AAC7C,EAAA,uBAAO,GAAA,CAAC,iBAAc,SAAA,EAAW,EAAA,CAAG,6BAA6B,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC1F;AAEO,SAAS,oBAAA,CAAqB;AAAA,EACnC,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA8C;AAC5C,EAAA,uBAAO,GAAA,CAAC,gBAAa,SAAA,EAAW,EAAA,CAAG,kBAAkB,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC9E;AAEO,SAAS,kBAAA,CAAmB;AAAA,EACjC,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAgC;AAC9B,EAAA,uBAAO,GAAA,CAAC,SAAI,SAAA,EAAW,EAAA,CAAG,iCAAiC,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AACpF;AAEO,SAAS,oBAAA,CAAqB;AAAA,EACnC,SAAA;AAAA,EACA,KAAA,GAAQ,KAAA;AAAA,EACR,GAAG;AACL,CAAA,EAAoE;AAClE,EAAA,uBACE,GAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA,CAAG,0BAAA,EAA4B,KAAA,IAAS,eAAe,SAAS,CAAA;AAAA,MAC1E,GAAG;AAAA;AAAA,GACN;AAEJ;AAKO,SAAS,oBAAA,CAAqB;AAAA,EACnC,OAAA,GAAU,KAAA;AAAA,EACV,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAoE;AAClE,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,UAAA;AAAA,QACA,OAAA,KAAY,UACR,kDAAA,GACA,kDAAA;AAAA,QACJ;AAAA,OACF;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ;AAKO,SAAS,oBAAA,CAAqB;AAAA,EACnC,OAAA,GAAU,KAAA;AAAA,EACV,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAoE;AAClE,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,UAAA;AAAA,QACA,OAAA,KAAY,UACR,2DAAA,GACA,+CAAA;AAAA,QACJ;AAAA,OACF;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ","file":"app-modal.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { DialogContent, DialogFooter, DialogHeader } from \"../ui/dialog\";\nimport { cn } from \"../ui/utils\";\n\n/** Shared shell for `DialogContent` when header/body/footer supply their own padding (`p-0`). */\nexport const appModalDialogContentChrome =\n \"bg-card border-border rounded-xl text-foreground shadow-xl gap-0 p-0 overflow-hidden\";\n\nexport function AppModalDialogContent({\n className,\n ...props\n}: React.ComponentProps<typeof DialogContent>) {\n return <DialogContent className={cn(appModalDialogContentChrome, className)} {...props} />;\n}\n\nexport function AppModalDialogHeader({\n className,\n ...props\n}: React.ComponentProps<typeof DialogHeader>) {\n return <DialogHeader className={cn(\"px-6 pt-6 pb-4\", className)} {...props} />;\n}\n\nexport function AppModalDialogBody({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return <div className={cn(\"px-6 pb-4 flex flex-col gap-4\", className)} {...props} />;\n}\n\nexport function AppModalDialogFooter({\n className,\n muted = false,\n ...props\n}: React.ComponentProps<typeof DialogFooter> & { muted?: boolean }) {\n return (\n <DialogFooter\n className={cn(\"px-6 py-4 gap-2 sm:gap-2\", muted && \"bg-muted/80\", className)}\n {...props}\n />\n );\n}\n\ntype ChromeHeaderVariant = \"row\" | \"stack\";\n\n/** Header region for Radix `Dialog.Content` layouts (custom close in header). */\nexport function AppModalChromeHeader({\n variant = \"row\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { variant?: ChromeHeaderVariant }) {\n return (\n <div\n className={cn(\n \"shrink-0\",\n variant === \"stack\"\n ? \"flex items-start justify-between gap-4 px-6 py-4\"\n : \"flex items-center justify-between px-6 pt-5 pb-4\",\n className,\n )}\n {...props}\n />\n );\n}\n\ntype ChromeFooterVariant = \"end\" | \"split\";\n\n/** Footer region for Radix `Dialog.Content` layouts. */\nexport function AppModalChromeFooter({\n variant = \"end\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { variant?: ChromeFooterVariant }) {\n return (\n <div\n className={cn(\n \"shrink-0\",\n variant === \"split\"\n ? \"flex items-center justify-between gap-2 px-6 py-4 bg-card\"\n : \"flex items-center justify-end gap-2 px-6 py-4\",\n className,\n )}\n {...props}\n />\n );\n}\n"]}
@@ -1,4 +1,3 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import * as React from 'react';
3
2
  import { ReactNode } from 'react';
4
3
 
@@ -24,6 +23,6 @@ type AppShellMobileNavProps = {
24
23
  * Bottom drawer + FAB used with {@link AppWebShell} on viewports below `md`
25
24
  * (sidebar and top bar hidden; main is full-bleed white).
26
25
  */
27
- declare function AppShellMobileNav({ open, onOpenChange, drawerHeader, drawerNav, drawerTitle, }: AppShellMobileNavProps): react_jsx_runtime.JSX.Element;
26
+ declare function AppShellMobileNav({ open, onOpenChange, drawerHeader, drawerNav, drawerTitle, }: AppShellMobileNavProps): React.JSX.Element;
28
27
 
29
28
  export { AppShellMobileNav, type AppShellMobileNavProps, useAppShellMobileNav };
@@ -1,73 +1,9 @@
1
+ import { Drawer, DrawerContent, DrawerTitle } from './chunk-VVXGQKN7.js';
2
+ import './chunk-DMPSQ22Q.js';
1
3
  import { Menu } from 'lucide-react';
2
4
  import { useState, useEffect } from 'react';
3
- import { Drawer as Drawer$1 } from 'vaul';
4
- import { clsx } from 'clsx';
5
- import { twMerge } from 'tailwind-merge';
6
5
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
7
6
 
8
- function cn(...inputs) {
9
- return twMerge(clsx(inputs));
10
- }
11
- function Drawer({ ...props }) {
12
- return /* @__PURE__ */ jsx(Drawer$1.Root, { "data-slot": "drawer", ...props });
13
- }
14
- function DrawerPortal({ ...props }) {
15
- return /* @__PURE__ */ jsx(Drawer$1.Portal, { "data-slot": "drawer-portal", ...props });
16
- }
17
- function DrawerOverlay({
18
- className,
19
- ...props
20
- }) {
21
- return /* @__PURE__ */ jsx(
22
- Drawer$1.Overlay,
23
- {
24
- "data-slot": "drawer-overlay",
25
- className: cn(
26
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
27
- className
28
- ),
29
- ...props
30
- }
31
- );
32
- }
33
- function DrawerContent({
34
- className,
35
- children,
36
- ...props
37
- }) {
38
- return /* @__PURE__ */ jsxs(DrawerPortal, { "data-slot": "drawer-portal", children: [
39
- /* @__PURE__ */ jsx(DrawerOverlay, {}),
40
- /* @__PURE__ */ jsxs(
41
- Drawer$1.Content,
42
- {
43
- "data-slot": "drawer-content",
44
- className: cn(
45
- "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
46
- "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
47
- "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
48
- "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
49
- "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
50
- className
51
- ),
52
- ...props,
53
- children: [
54
- /* @__PURE__ */ jsx("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
55
- children
56
- ]
57
- }
58
- )
59
- ] });
60
- }
61
- function DrawerTitle({ className, ...props }) {
62
- return /* @__PURE__ */ jsx(
63
- Drawer$1.Title,
64
- {
65
- "data-slot": "drawer-title",
66
- className: cn("text-foreground font-semibold", className),
67
- ...props
68
- }
69
- );
70
- }
71
7
  function useAppShellMobileNav(pathname) {
72
8
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
73
9
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ui/utils.ts","../src/ui/drawer.tsx","../src/shell/app-shell-mobile-nav.tsx"],"names":["DrawerPrimitive","jsxs","jsx"],"mappings":"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","file":"app-shell-mobile-nav.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\n\nimport { cn } from \"./utils\";\n\nfunction Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />;\n}\n\nfunction DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />;\n}\n\nfunction DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />;\n}\n\nfunction DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />;\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DrawerContent({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Content>) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n \"group/drawer-content bg-background fixed z-50 flex h-auto flex-col\",\n \"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b\",\n \"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t\",\n \"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n \"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm\",\n className,\n )}\n {...props}\n >\n <div className=\"bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n}\n\nfunction DrawerHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\"flex flex-col gap-1.5 p-4\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-footer\"\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\"text-foreground font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Description>) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Drawer,\n DrawerPortal,\n DrawerOverlay,\n DrawerTrigger,\n DrawerClose,\n DrawerContent,\n DrawerHeader,\n DrawerFooter,\n DrawerTitle,\n DrawerDescription,\n};\n","\"use client\";\n\nimport { Menu } from \"lucide-react\";\nimport { useEffect, useState, type ReactNode } from \"react\";\nimport { Drawer, DrawerContent, DrawerTitle } from \"../ui/drawer\";\n\n/**\n * Closes the mobile nav drawer on route change and when crossing the `md` breakpoint upward.\n * Pass `location.pathname` from React Router (or any stable route key).\n */\nexport function useAppShellMobileNav(pathname: string) {\n const [mobileNavOpen, setMobileNavOpen] = useState(false);\n\n useEffect(() => {\n setMobileNavOpen(false);\n }, [pathname]);\n\n useEffect(() => {\n const mq = window.matchMedia(\"(min-width: 768px)\");\n const closeIfDesktop = () => {\n if (mq.matches) setMobileNavOpen(false);\n };\n mq.addEventListener(\"change\", closeIfDesktop);\n closeIfDesktop();\n return () => mq.removeEventListener(\"change\", closeIfDesktop);\n }, []);\n\n return { mobileNavOpen, setMobileNavOpen };\n}\n\nexport type AppShellMobileNavProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Top block inside the drawer (brand, workspace controls, sign out, etc.). */\n drawerHeader: ReactNode;\n /** Scrollable navigation — usually the same content as `AppWebShell`’s `sidebar`. */\n drawerNav: ReactNode;\n /** Accessible name for the drawer (visually hidden). */\n drawerTitle?: string;\n};\n\n/**\n * Bottom drawer + FAB used with {@link AppWebShell} on viewports below `md`\n * (sidebar and top bar hidden; main is full-bleed white).\n */\nexport function AppShellMobileNav({\n open,\n onOpenChange,\n drawerHeader,\n drawerNav,\n drawerTitle = \"Menu\",\n}: AppShellMobileNavProps) {\n return (\n <>\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent className=\"gap-0 border-sidebar-border bg-sidebar px-0 pt-0 pb-[max(1rem,env(safe-area-inset-bottom))] text-sidebar-foreground\">\n <DrawerTitle className=\"sr-only\">{drawerTitle}</DrawerTitle>\n <div className=\"flex max-h-[min(85vh,calc(100dvh-3rem))] min-h-0 flex-col\">\n <div className=\"shrink-0 space-y-3 border-b border-sidebar-border bg-sidebar px-4 pt-2 pb-4\">\n {drawerHeader}\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto py-2\">{drawerNav}</div>\n </div>\n </DrawerContent>\n </Drawer>\n\n <button\n type=\"button\"\n className=\"fixed z-40 flex size-14 items-center justify-center rounded-full bg-blue-600 text-white shadow-lg ring-1 ring-black/5 transition-colors hover:bg-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 md:hidden\"\n style={{\n bottom: \"max(1rem, env(safe-area-inset-bottom))\",\n right: \"max(1rem, env(safe-area-inset-right))\",\n }}\n aria-label=\"Open navigation\"\n aria-expanded={open}\n onClick={() => onOpenChange(true)}\n >\n <Menu size={26} strokeWidth={2.25} aria-hidden />\n </button>\n </>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/shell/app-shell-mobile-nav.tsx"],"names":[],"mappings":";;;;;;AAUO,SAAS,qBAAqB,QAAA,EAAkB;AACrD,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAExD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,gBAAA,CAAiB,KAAK,CAAA;AAAA,EACxB,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,UAAA,CAAW,oBAAoB,CAAA;AACjD,IAAA,MAAM,iBAAiB,MAAM;AAC3B,MAAA,IAAI,EAAA,CAAG,OAAA,EAAS,gBAAA,CAAiB,KAAK,CAAA;AAAA,IACxC,CAAA;AACA,IAAA,EAAA,CAAG,gBAAA,CAAiB,UAAU,cAAc,CAAA;AAC5C,IAAA,cAAA,EAAe;AACf,IAAA,OAAO,MAAM,EAAA,CAAG,mBAAA,CAAoB,QAAA,EAAU,cAAc,CAAA;AAAA,EAC9D,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,eAAe,gBAAA,EAAiB;AAC3C;AAiBO,SAAS,iBAAA,CAAkB;AAAA,EAChC,IAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA,GAAc;AAChB,CAAA,EAA2B;AACzB,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAO,IAAA,EAAY,YAAA,EAClB,QAAA,kBAAA,IAAA,CAAC,aAAA,EAAA,EAAc,WAAU,qHAAA,EACvB,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,WAAA,EAAY,CAAA;AAAA,sBAC9C,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2DAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6EAAA,EACZ,QAAA,EAAA,YAAA,EACH,CAAA;AAAA,wBACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAA,EAAuC,QAAA,EAAA,SAAA,EAAU;AAAA,OAAA,EAClE;AAAA,KAAA,EACF,CAAA,EACF,CAAA;AAAA,oBAEA,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,SAAA,EAAU,iRAAA;AAAA,QACV,KAAA,EAAO;AAAA,UACL,MAAA,EAAQ,wCAAA;AAAA,UACR,KAAA,EAAO;AAAA,SACT;AAAA,QACA,YAAA,EAAW,iBAAA;AAAA,QACX,eAAA,EAAe,IAAA;AAAA,QACf,OAAA,EAAS,MAAM,YAAA,CAAa,IAAI,CAAA;AAAA,QAEhC,8BAAC,IAAA,EAAA,EAAK,IAAA,EAAM,IAAI,WAAA,EAAa,IAAA,EAAM,eAAW,IAAA,EAAC;AAAA;AAAA;AACjD,GAAA,EACF,CAAA;AAEJ","file":"app-shell-mobile-nav.js","sourcesContent":["\"use client\";\n\nimport { Menu } from \"lucide-react\";\nimport { useEffect, useState, type ReactNode } from \"react\";\nimport { Drawer, DrawerContent, DrawerTitle } from \"../ui/drawer\";\n\n/**\n * Closes the mobile nav drawer on route change and when crossing the `md` breakpoint upward.\n * Pass `location.pathname` from React Router (or any stable route key).\n */\nexport function useAppShellMobileNav(pathname: string) {\n const [mobileNavOpen, setMobileNavOpen] = useState(false);\n\n useEffect(() => {\n setMobileNavOpen(false);\n }, [pathname]);\n\n useEffect(() => {\n const mq = window.matchMedia(\"(min-width: 768px)\");\n const closeIfDesktop = () => {\n if (mq.matches) setMobileNavOpen(false);\n };\n mq.addEventListener(\"change\", closeIfDesktop);\n closeIfDesktop();\n return () => mq.removeEventListener(\"change\", closeIfDesktop);\n }, []);\n\n return { mobileNavOpen, setMobileNavOpen };\n}\n\nexport type AppShellMobileNavProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Top block inside the drawer (brand, workspace controls, sign out, etc.). */\n drawerHeader: ReactNode;\n /** Scrollable navigation — usually the same content as `AppWebShell`’s `sidebar`. */\n drawerNav: ReactNode;\n /** Accessible name for the drawer (visually hidden). */\n drawerTitle?: string;\n};\n\n/**\n * Bottom drawer + FAB used with {@link AppWebShell} on viewports below `md`\n * (sidebar and top bar hidden; main is full-bleed white).\n */\nexport function AppShellMobileNav({\n open,\n onOpenChange,\n drawerHeader,\n drawerNav,\n drawerTitle = \"Menu\",\n}: AppShellMobileNavProps) {\n return (\n <>\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent className=\"gap-0 border-sidebar-border bg-sidebar px-0 pt-0 pb-[max(1rem,env(safe-area-inset-bottom))] text-sidebar-foreground\">\n <DrawerTitle className=\"sr-only\">{drawerTitle}</DrawerTitle>\n <div className=\"flex max-h-[min(85vh,calc(100dvh-3rem))] min-h-0 flex-col\">\n <div className=\"shrink-0 space-y-3 border-b border-sidebar-border bg-sidebar px-4 pt-2 pb-4\">\n {drawerHeader}\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto py-2\">{drawerNav}</div>\n </div>\n </DrawerContent>\n </Drawer>\n\n <button\n type=\"button\"\n className=\"fixed z-40 flex size-14 items-center justify-center rounded-full bg-blue-600 text-white shadow-lg ring-1 ring-black/5 transition-colors hover:bg-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 md:hidden\"\n style={{\n bottom: \"max(1rem, env(safe-area-inset-bottom))\",\n right: \"max(1rem, env(safe-area-inset-right))\",\n }}\n aria-label=\"Open navigation\"\n aria-expanded={open}\n onClick={() => onOpenChange(true)}\n >\n <Menu size={26} strokeWidth={2.25} aria-hidden />\n </button>\n </>\n );\n}\n"]}
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  type AppWebShellProps = {
@@ -15,6 +15,6 @@ type AppWebShellProps = {
15
15
  * Shared app chrome: outer shell + sidebar, optional top bar, and inset main on desktop.
16
16
  * Keeps operator / member (and future web apps) visually aligned.
17
17
  */
18
- declare function AppWebShell({ topBar, sidebar, sidebarCollapsed, children, }: AppWebShellProps): react_jsx_runtime.JSX.Element;
18
+ declare function AppWebShell({ topBar, sidebar, sidebarCollapsed, children, }: AppWebShellProps): React.JSX.Element;
19
19
 
20
20
  export { AppWebShell, type AppWebShellProps };
@@ -1,10 +1,7 @@
1
+ import { getShellTheme } from './chunk-OWNUMPUC.js';
1
2
  import { useEffect } from 'react';
2
3
  import { jsxs, jsx } from 'react/jsx-runtime';
3
4
 
4
- // src/shell/app-web-shell.tsx
5
- function getShellTheme() {
6
- return "light";
7
- }
8
5
  function AppWebShell({
9
6
  topBar,
10
7
  sidebar,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/theme/shell-theme.ts","../src/shell/app-web-shell.tsx"],"names":[],"mappings":";;;;AAKO,SAAS,aAAA,GAA4B;AAC1C,EAAA,OAAuC,OAAA;AACzC;ACWO,SAAS,WAAA,CAAY;AAAA,EAC1B,MAAA;AAAA,EACA,OAAA;AAAA,EACA,gBAAA,GAAmB,IAAA;AAAA,EACnB;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,aAAa,aAAA,EAAc;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,QAAQ,UAAA,GAAa,UAAA;AAC9C,IAAA,OAAO,MAAM;AACX,MAAA,OAAO,QAAA,CAAS,gBAAgB,OAAA,CAAQ,UAAA;AAAA,IAC1C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,kBAAA,EAAkB,UAAA;AAAA,MAClB,SAAA,EAAU,6EAAA;AAAA,MAET,QAAA,EAAA;AAAA,QAAA,MAAA,IAAU,uBACT,GAAA,CAAC,QAAA,EAAA,EAAO,SAAA,EAAU,wGAAA,EACf,kBACH,CAAA,GACE,IAAA;AAAA,wBAEJ,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6BAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,CAAA,0HAAA,EACT,gBAAA,GAAmB,MAAA,GAAS,aAC9B,CAAA,CAAA;AAAA,cAEA,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAA,EAAuC,QAAA,EAAA,OAAA,EAAQ;AAAA;AAAA,WAChE;AAAA,0BAEA,GAAA,CAAC,SAAI,SAAA,EAAU,qDAAA,EACb,8BAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qIAAA,EACb,QAAA,EACH,CAAA,EACF;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ","file":"app-web-shell.js","sourcesContent":["/** Flip to compare light vs dark outer shell with the team. */\nexport const SHELL_USE_DARK_THEME = false;\n\nexport type ShellTheme = \"light\" | \"dark\";\n\nexport function getShellTheme(): ShellTheme {\n return SHELL_USE_DARK_THEME ? \"dark\" : \"light\";\n}\n","import { useEffect, type ReactNode } from \"react\";\nimport { getShellTheme } from \"../theme/shell-theme\";\n\nexport type AppWebShellProps = {\n /** Full-width bar above the sidebar + main row (e.g. brand, account actions). */\n topBar?: ReactNode;\n /** Left navigation column. */\n sidebar: ReactNode;\n /** Collapses the desktop sidebar to an icon rail. */\n sidebarCollapsed?: boolean;\n /** Routed page content — typically a card area on desktop. */\n children: ReactNode;\n};\n\n/**\n * Shared app chrome: outer shell + sidebar, optional top bar, and inset main on desktop.\n * Keeps operator / member (and future web apps) visually aligned.\n */\nexport function AppWebShell({\n topBar,\n sidebar,\n sidebarCollapsed = true,\n children,\n}: AppWebShellProps) {\n const shellTheme = getShellTheme();\n\n useEffect(() => {\n document.documentElement.dataset.shellTheme = shellTheme;\n return () => {\n delete document.documentElement.dataset.shellTheme;\n };\n }, [shellTheme]);\n\n return (\n <div\n data-shell-theme={shellTheme}\n className=\"flex h-screen w-screen flex-col overflow-hidden bg-background md:bg-sidebar\"\n >\n {topBar != null ? (\n <header className=\"z-10 hidden h-11 shrink-0 items-center justify-between bg-sidebar px-4 text-sidebar-foreground md:flex\">\n {topBar}\n </header>\n ) : null}\n\n <div className=\"flex min-h-0 min-w-0 flex-1\">\n <aside\n className={`hidden h-full min-h-0 shrink-0 flex-col bg-sidebar py-2.5 text-sidebar-foreground transition-[width] duration-200 md:flex ${\n sidebarCollapsed ? \"w-16\" : \"w-[14.4rem]\"\n }`}\n >\n <div className=\"flex min-h-0 w-full flex-1 flex-col\">{sidebar}</div>\n </aside>\n\n <div className=\"flex min-h-0 min-w-0 flex-1 p-0 md:pr-2.5 md:pb-2.5\">\n <main className=\"flex min-h-0 w-full min-w-0 flex-1 flex-col overflow-hidden rounded-none border-0 bg-card md:rounded-2xl md:border md:border-border\">\n {children}\n </main>\n </div>\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/shell/app-web-shell.tsx"],"names":[],"mappings":";;;;AAkBO,SAAS,WAAA,CAAY;AAAA,EAC1B,MAAA;AAAA,EACA,OAAA;AAAA,EACA,gBAAA,GAAmB,IAAA;AAAA,EACnB;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,aAAa,aAAA,EAAc;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,QAAQ,UAAA,GAAa,UAAA;AAC9C,IAAA,OAAO,MAAM;AACX,MAAA,OAAO,QAAA,CAAS,gBAAgB,OAAA,CAAQ,UAAA;AAAA,IAC1C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,kBAAA,EAAkB,UAAA;AAAA,MAClB,SAAA,EAAU,6EAAA;AAAA,MAET,QAAA,EAAA;AAAA,QAAA,MAAA,IAAU,uBACT,GAAA,CAAC,QAAA,EAAA,EAAO,SAAA,EAAU,wGAAA,EACf,kBACH,CAAA,GACE,IAAA;AAAA,wBAEJ,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6BAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,CAAA,0HAAA,EACT,gBAAA,GAAmB,MAAA,GAAS,aAC9B,CAAA,CAAA;AAAA,cAEA,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAA,EAAuC,QAAA,EAAA,OAAA,EAAQ;AAAA;AAAA,WAChE;AAAA,0BAEA,GAAA,CAAC,SAAI,SAAA,EAAU,qDAAA,EACb,8BAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qIAAA,EACb,QAAA,EACH,CAAA,EACF;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ","file":"app-web-shell.js","sourcesContent":["import { useEffect, type ReactNode } from \"react\";\nimport { getShellTheme } from \"../theme/shell-theme\";\n\nexport type AppWebShellProps = {\n /** Full-width bar above the sidebar + main row (e.g. brand, account actions). */\n topBar?: ReactNode;\n /** Left navigation column. */\n sidebar: ReactNode;\n /** Collapses the desktop sidebar to an icon rail. */\n sidebarCollapsed?: boolean;\n /** Routed page content — typically a card area on desktop. */\n children: ReactNode;\n};\n\n/**\n * Shared app chrome: outer shell + sidebar, optional top bar, and inset main on desktop.\n * Keeps operator / member (and future web apps) visually aligned.\n */\nexport function AppWebShell({\n topBar,\n sidebar,\n sidebarCollapsed = true,\n children,\n}: AppWebShellProps) {\n const shellTheme = getShellTheme();\n\n useEffect(() => {\n document.documentElement.dataset.shellTheme = shellTheme;\n return () => {\n delete document.documentElement.dataset.shellTheme;\n };\n }, [shellTheme]);\n\n return (\n <div\n data-shell-theme={shellTheme}\n className=\"flex h-screen w-screen flex-col overflow-hidden bg-background md:bg-sidebar\"\n >\n {topBar != null ? (\n <header className=\"z-10 hidden h-11 shrink-0 items-center justify-between bg-sidebar px-4 text-sidebar-foreground md:flex\">\n {topBar}\n </header>\n ) : null}\n\n <div className=\"flex min-h-0 min-w-0 flex-1\">\n <aside\n className={`hidden h-full min-h-0 shrink-0 flex-col bg-sidebar py-2.5 text-sidebar-foreground transition-[width] duration-200 md:flex ${\n sidebarCollapsed ? \"w-16\" : \"w-[14.4rem]\"\n }`}\n >\n <div className=\"flex min-h-0 w-full flex-1 flex-col\">{sidebar}</div>\n </aside>\n\n <div className=\"flex min-h-0 min-w-0 flex-1 p-0 md:pr-2.5 md:pb-2.5\">\n <main className=\"flex min-h-0 w-full min-w-0 flex-1 flex-col overflow-hidden rounded-none border-0 bg-card md:rounded-2xl md:border md:border-border\">\n {children}\n </main>\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as React$1 from 'react';
2
2
 
3
3
  /**
4
4
  * Date / schedule helpers used by booking-schedule calendars.
@@ -32,55 +32,55 @@ interface BookingState {
32
32
 
33
33
  declare function ScheduleFieldLabel({ children }: {
34
34
  children: React.ReactNode;
35
- }): react_jsx_runtime.JSX.Element;
35
+ }): React$1.JSX.Element;
36
36
  declare function ScheduleInfoBanner({ children }: {
37
37
  children: React.ReactNode;
38
- }): react_jsx_runtime.JSX.Element;
38
+ }): React$1.JSX.Element;
39
39
  declare function ScheduleStepper({ value, min, max, onChange, }: {
40
40
  value: number;
41
41
  min?: number;
42
42
  max?: number;
43
43
  onChange: (v: number) => void;
44
- }): react_jsx_runtime.JSX.Element;
44
+ }): React$1.JSX.Element;
45
45
  declare function WeekCalendarPicker({ weekFrom, onChange, flat, }: {
46
46
  weekFrom: string;
47
47
  onChange: (v: string) => void;
48
48
  flat?: boolean;
49
- }): react_jsx_runtime.JSX.Element;
49
+ }): React$1.JSX.Element;
50
50
  declare function MonthRangeCalendar({ monthFrom, monthTo, onChange, flat, }: {
51
51
  monthFrom: string;
52
52
  monthTo: string;
53
53
  onChange: (from: string, to: string) => void;
54
54
  flat?: boolean;
55
- }): react_jsx_runtime.JSX.Element;
55
+ }): React$1.JSX.Element;
56
56
  declare function MonthPeriodPicker({ booking, onChange, flat, }: {
57
57
  booking: BookingState;
58
58
  onChange: (patch: Partial<BookingState>) => void;
59
59
  flat?: boolean;
60
- }): react_jsx_runtime.JSX.Element;
60
+ }): React$1.JSX.Element;
61
61
  declare function WeekPeriodPicker({ booking, onChange, flat, }: {
62
62
  booking: BookingState;
63
63
  onChange: (patch: Partial<BookingState>) => void;
64
64
  flat?: boolean;
65
- }): react_jsx_runtime.JSX.Element;
65
+ }): React$1.JSX.Element;
66
66
  declare function DayCalendarPicker({ selectedDate, onChange, minDate, flat, }: {
67
67
  selectedDate: string;
68
68
  onChange: (date: string) => void;
69
69
  minDate?: string;
70
70
  flat?: boolean;
71
- }): react_jsx_runtime.JSX.Element;
71
+ }): React$1.JSX.Element;
72
72
  declare function DayRangeCalendar({ startDate, endDate, onChange, minDate, flat, }: {
73
73
  startDate: string;
74
74
  endDate: string;
75
75
  onChange: (start: string, end: string) => void;
76
76
  minDate?: string;
77
77
  flat?: boolean;
78
- }): react_jsx_runtime.JSX.Element;
78
+ }): React$1.JSX.Element;
79
79
  declare function DayPeriodPicker({ booking, onChange, flat, }: {
80
80
  booking: BookingState;
81
81
  onChange: (patch: Partial<BookingState>) => void;
82
82
  flat?: boolean;
83
- }): react_jsx_runtime.JSX.Element;
83
+ }): React$1.JSX.Element;
84
84
  declare function HourlySchedulePicker({ scheduleDate, scheduleTime, onChange, timeFormat, flat, }: {
85
85
  scheduleDate: string;
86
86
  scheduleTime: string;
@@ -90,6 +90,6 @@ declare function HourlySchedulePicker({ scheduleDate, scheduleTime, onChange, ti
90
90
  }) => void;
91
91
  timeFormat?: "12h" | "24h";
92
92
  flat?: boolean;
93
- }): react_jsx_runtime.JSX.Element;
93
+ }): React$1.JSX.Element;
94
94
 
95
95
  export { DayCalendarPicker, DayPeriodPicker, DayRangeCalendar, HourlySchedulePicker, MonthPeriodPicker, MonthRangeCalendar, ScheduleFieldLabel, ScheduleInfoBanner, ScheduleStepper, WeekCalendarPicker, WeekPeriodPicker };