@spacesync/client 0.1.0 → 0.1.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 (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 +2 -2
  78. package/dist/image-picker-field.js +3 -48
  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/forms.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/errors.ts","../src/forms/useApiForm.ts"],"names":[],"mappings":";;;;;;AAwBO,IAAM,cAAA,GAAN,MAAM,eAAA,SAAuB,KAAA,CAAM;AAAA,EAC/B,IAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,WAAA;AAAA,EAET,WAAA,CAAY,IAAA,EAA0B,OAAA,EAAiB,OAAA,GAAiC,EAAC,EAAG;AAC1F,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AACvB,IAAA,IAAA,CAAK,cAAc,OAAA,CAAQ,WAAA;AAC3B,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,eAAA,CAAe,SAAS,CAAA;AAAA,EACtD;AACF,CAAA;AAEO,SAAS,iBAAiB,GAAA,EAAqC;AACpE,EAAA,OAAO,GAAA,YAAe,cAAA;AACxB;;;ACLO,SAAS,UAAA,CAAsC,QAAiB,OAAA,EAA0C;AAK/G,EAAA,MAAM,IAAA,GAAO,QAA0B,EAAE,QAAA,EAAU,YAAY,MAAe,CAAA,EAAG,GAAG,OAAA,EAAS,CAAA;AAE7F,EAAA,MAAM,gBAAgB,CACpB,OAAA,EACA,kBAEA,IAAA,CAAK,YAAA,CAAa,OAAO,MAAA,KAAW;AAClC,IAAA,IAAI;AACF,MAAA,MAAM,QAAQ,MAAM,CAAA;AAAA,IACtB,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,CAAC,gBAAA,CAAiB,GAAG,CAAA,EAAG,MAAM,GAAA;AAElC,MAAA,IAAI,aAAA,EAAe,UAAA,GAAa,GAAA,EAAK,IAAI,CAAA,EAAG;AAE5C,MAAA,QAAQ,IAAI,IAAA;AAAM,QAChB,KAAK,SAAA;AACH,UAAA,IAAA,CAAK,QAAA,CAAS,QAAQ,EAAE,IAAA,EAAM,WAAW,OAAA,EAAS,GAAA,CAAI,SAAS,CAAA;AAC/D,UAAA;AAAA,QACF,KAAK,YAAA,EAAc;AACjB,UAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,MAAA,CAAO,KAAK,IAAA,CAAK,SAAA,EAAW,CAAC,CAAA;AACzD,UAAA,IAAI,SAAA,GAAY,KAAA;AAChB,UAAA,KAAA,MAAW,UAAA,IAAc,GAAA,CAAI,WAAA,IAAe,EAAC,EAAG;AAC9C,YAAA,IAAI,WAAA,CAAY,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACpC,cAAA,IAAA,CAAK,QAAA,CAAS,WAAW,IAAA,EAAqC;AAAA,gBAC5D,IAAA,EAAM,QAAA;AAAA,gBACN,SAAS,UAAA,CAAW;AAAA,eACrB,CAAA;AACD,cAAA,SAAA,GAAY,IAAA;AAAA,YACd;AAAA,UACF;AACA,UAAA,IAAI,CAAC,SAAA,EAAW,IAAA,CAAK,QAAA,CAAS,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,GAAA,CAAI,OAAA,EAAS,CAAA;AAC9E,UAAA;AAAA,QACF;AAAA,QACA,KAAK,MAAA;AAAA,QACL;AACE,UAAA,IAAA,CAAK,QAAA,CAAS,QAAQ,EAAE,IAAA,EAAM,UAAU,OAAA,EAAS,GAAA,CAAI,SAAS,CAAA;AAC9D,UAAA;AAAA;AACJ,IACF;AAAA,EACF,CAAC,CAAA;AAEH,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,aAAA,EAAc;AAClC","file":"forms.js","sourcesContent":["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","import { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n useForm,\n type FieldPath,\n type FieldValues,\n type UseFormProps,\n type UseFormReturn,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\nimport { isApiClientError, type ApiClientError } from \"../api/errors\";\n\nexport interface SubmitWithApiOptions<TFieldValues extends FieldValues> {\n /**\n * Called before the default per-`kind` handling below. Return `true` if you fully handled the\n * error yourself (e.g. mapped a 409 \"email already registered\" onto the `email` field) - this\n * skips the default handling. Return `false`/`undefined` to fall back to it.\n */\n onApiError?: (error: ApiClientError, form: UseFormReturn<TFieldValues>) => boolean | void;\n}\n\n/**\n * Wraps `react-hook-form` + the zod resolver, and gives `submitWithApi` a default error-handling\n * policy per `ApiClientError.kind`:\n *\n * - client-side validation (bad email, missing field) never reaches here - `zodResolver` blocks the\n * submit and populates per-field errors before `onValid` runs.\n * - \"network\" (server unreachable) -> root error, generic \"can't reach the server\" wording.\n * - \"validation\" (server-side 400 with field-mappable issues) -> mapped onto matching fields by name;\n * anything that can't be matched falls back to a root error.\n * - \"http\" (401/403/404/409/500, etc.) -> root error by default; not reliably field-mappable from the\n * backend's response shape, so a specific form can opt in via `onApiError` (e.g. map a register 409\n * onto the `email` field).\n *\n * A 401 caused by an expired session on an *authenticated* call has already been resolved one layer\n * down, inside `createApiClient`'s response interceptor (silent refresh-and-retry) - by the time an\n * error reaches here, refresh already failed and the app-level `onAuthExpired` callback already fired,\n * so this hook doesn't need auth-specific branching of its own.\n */\nexport function useApiForm<TSchema extends z.ZodType>(schema: TSchema, options?: UseFormProps<z.infer<TSchema>>) {\n // `zodResolver`'s overloads require a concrete Zod schema type it can pattern-match against\n // (Zod v3's `_def.typeName` vs. v4's `_zod`), which a generic `TSchema extends z.ZodType` can't\n // structurally prove even though it's always a real schema at runtime - this is a documented\n // typing limitation of `@hookform/resolvers`, not unsafe in practice.\n const form = useForm<z.infer<TSchema>>({ resolver: zodResolver(schema as never), ...options });\n\n const submitWithApi = (\n onValid: (values: z.infer<TSchema>) => Promise<void>,\n submitOptions?: SubmitWithApiOptions<z.infer<TSchema>>,\n ) =>\n form.handleSubmit(async (values) => {\n try {\n await onValid(values);\n } catch (err) {\n if (!isApiClientError(err)) throw err; // programmer bug - don't swallow, let it surface normally\n\n if (submitOptions?.onApiError?.(err, form)) return; // caller fully handled it\n\n switch (err.kind) {\n case \"network\":\n form.setError(\"root\", { type: \"network\", message: err.message });\n break;\n case \"validation\": {\n const knownFields = new Set(Object.keys(form.getValues()));\n let mappedAny = false;\n for (const fieldError of err.fieldErrors ?? []) {\n if (knownFields.has(fieldError.path)) {\n form.setError(fieldError.path as FieldPath<z.infer<TSchema>>, {\n type: \"server\",\n message: fieldError.message,\n });\n mappedAny = true;\n }\n }\n if (!mappedAny) form.setError(\"root\", { type: \"server\", message: err.message });\n break;\n }\n case \"http\":\n default:\n form.setError(\"root\", { type: \"server\", message: err.message });\n break;\n }\n }\n });\n\n return { ...form, submitWithApi };\n}\n"]}
1
+ {"version":3,"sources":["../src/forms/useApiForm.ts"],"names":[],"mappings":";;;;AAsCO,SAAS,UAAA,CAAsC,QAAiB,OAAA,EAA0C;AAK/G,EAAA,MAAM,IAAA,GAAO,QAA0B,EAAE,QAAA,EAAU,YAAY,MAAe,CAAA,EAAG,GAAG,OAAA,EAAS,CAAA;AAE7F,EAAA,MAAM,gBAAgB,CACpB,OAAA,EACA,kBAEA,IAAA,CAAK,YAAA,CAAa,OAAO,MAAA,KAAW;AAClC,IAAA,IAAI;AACF,MAAA,MAAM,QAAQ,MAAM,CAAA;AAAA,IACtB,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,CAAC,gBAAA,CAAiB,GAAG,CAAA,EAAG,MAAM,GAAA;AAElC,MAAA,IAAI,aAAA,EAAe,UAAA,GAAa,GAAA,EAAK,IAAI,CAAA,EAAG;AAE5C,MAAA,QAAQ,IAAI,IAAA;AAAM,QAChB,KAAK,SAAA;AACH,UAAA,IAAA,CAAK,QAAA,CAAS,QAAQ,EAAE,IAAA,EAAM,WAAW,OAAA,EAAS,GAAA,CAAI,SAAS,CAAA;AAC/D,UAAA;AAAA,QACF,KAAK,YAAA,EAAc;AACjB,UAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,MAAA,CAAO,KAAK,IAAA,CAAK,SAAA,EAAW,CAAC,CAAA;AACzD,UAAA,IAAI,SAAA,GAAY,KAAA;AAChB,UAAA,KAAA,MAAW,UAAA,IAAc,GAAA,CAAI,WAAA,IAAe,EAAC,EAAG;AAC9C,YAAA,IAAI,WAAA,CAAY,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACpC,cAAA,IAAA,CAAK,QAAA,CAAS,WAAW,IAAA,EAAqC;AAAA,gBAC5D,IAAA,EAAM,QAAA;AAAA,gBACN,SAAS,UAAA,CAAW;AAAA,eACrB,CAAA;AACD,cAAA,SAAA,GAAY,IAAA;AAAA,YACd;AAAA,UACF;AACA,UAAA,IAAI,CAAC,SAAA,EAAW,IAAA,CAAK,QAAA,CAAS,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,GAAA,CAAI,OAAA,EAAS,CAAA;AAC9E,UAAA;AAAA,QACF;AAAA,QACA,KAAK,MAAA;AAAA,QACL;AACE,UAAA,IAAA,CAAK,QAAA,CAAS,QAAQ,EAAE,IAAA,EAAM,UAAU,OAAA,EAAS,GAAA,CAAI,SAAS,CAAA;AAC9D,UAAA;AAAA;AACJ,IACF;AAAA,EACF,CAAC,CAAA;AAEH,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,aAAA,EAAc;AAClC","file":"forms.js","sourcesContent":["import { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n useForm,\n type FieldPath,\n type FieldValues,\n type UseFormProps,\n type UseFormReturn,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\nimport { isApiClientError, type ApiClientError } from \"../api/errors\";\n\nexport interface SubmitWithApiOptions<TFieldValues extends FieldValues> {\n /**\n * Called before the default per-`kind` handling below. Return `true` if you fully handled the\n * error yourself (e.g. mapped a 409 \"email already registered\" onto the `email` field) - this\n * skips the default handling. Return `false`/`undefined` to fall back to it.\n */\n onApiError?: (error: ApiClientError, form: UseFormReturn<TFieldValues>) => boolean | void;\n}\n\n/**\n * Wraps `react-hook-form` + the zod resolver, and gives `submitWithApi` a default error-handling\n * policy per `ApiClientError.kind`:\n *\n * - client-side validation (bad email, missing field) never reaches here - `zodResolver` blocks the\n * submit and populates per-field errors before `onValid` runs.\n * - \"network\" (server unreachable) -> root error, generic \"can't reach the server\" wording.\n * - \"validation\" (server-side 400 with field-mappable issues) -> mapped onto matching fields by name;\n * anything that can't be matched falls back to a root error.\n * - \"http\" (401/403/404/409/500, etc.) -> root error by default; not reliably field-mappable from the\n * backend's response shape, so a specific form can opt in via `onApiError` (e.g. map a register 409\n * onto the `email` field).\n *\n * A 401 caused by an expired session on an *authenticated* call has already been resolved one layer\n * down, inside `createApiClient`'s response interceptor (silent refresh-and-retry) - by the time an\n * error reaches here, refresh already failed and the app-level `onAuthExpired` callback already fired,\n * so this hook doesn't need auth-specific branching of its own.\n */\nexport function useApiForm<TSchema extends z.ZodType>(schema: TSchema, options?: UseFormProps<z.infer<TSchema>>) {\n // `zodResolver`'s overloads require a concrete Zod schema type it can pattern-match against\n // (Zod v3's `_def.typeName` vs. v4's `_zod`), which a generic `TSchema extends z.ZodType` can't\n // structurally prove even though it's always a real schema at runtime - this is a documented\n // typing limitation of `@hookform/resolvers`, not unsafe in practice.\n const form = useForm<z.infer<TSchema>>({ resolver: zodResolver(schema as never), ...options });\n\n const submitWithApi = (\n onValid: (values: z.infer<TSchema>) => Promise<void>,\n submitOptions?: SubmitWithApiOptions<z.infer<TSchema>>,\n ) =>\n form.handleSubmit(async (values) => {\n try {\n await onValid(values);\n } catch (err) {\n if (!isApiClientError(err)) throw err; // programmer bug - don't swallow, let it surface normally\n\n if (submitOptions?.onApiError?.(err, form)) return; // caller fully handled it\n\n switch (err.kind) {\n case \"network\":\n form.setError(\"root\", { type: \"network\", message: err.message });\n break;\n case \"validation\": {\n const knownFields = new Set(Object.keys(form.getValues()));\n let mappedAny = false;\n for (const fieldError of err.fieldErrors ?? []) {\n if (knownFields.has(fieldError.path)) {\n form.setError(fieldError.path as FieldPath<z.infer<TSchema>>, {\n type: \"server\",\n message: fieldError.message,\n });\n mappedAny = true;\n }\n }\n if (!mappedAny) form.setError(\"root\", { type: \"server\", message: err.message });\n break;\n }\n case \"http\":\n default:\n form.setError(\"root\", { type: \"server\", message: err.message });\n break;\n }\n }\n });\n\n return { ...form, submitWithApi };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as React from 'react';
2
2
 
3
3
  declare function ImagePickerField({ label, value, onChange, description, previewAlt, }: {
4
4
  label?: string;
@@ -6,6 +6,6 @@ declare function ImagePickerField({ label, value, onChange, description, preview
6
6
  onChange: (value: string) => void;
7
7
  description?: string;
8
8
  previewAlt?: string;
9
- }): react_jsx_runtime.JSX.Element;
9
+ }): React.JSX.Element;
10
10
 
11
11
  export { ImagePickerField };
@@ -1,54 +1,9 @@
1
- import * as React from 'react';
1
+ import { Button } from './chunk-VHINHHZQ.js';
2
+ import './chunk-DMPSQ22Q.js';
2
3
  import { useRef } from 'react';
3
4
  import { ImagePlus, Trash2, Upload } from 'lucide-react';
4
- import { Slot } from '@radix-ui/react-slot';
5
- import { cva } from 'class-variance-authority';
6
- import { clsx } from 'clsx';
7
- import { twMerge } from 'tailwind-merge';
8
- import { jsx, jsxs } from 'react/jsx-runtime';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
9
6
 
10
- // src/files/image-picker-field.tsx
11
- function cn(...inputs) {
12
- return twMerge(clsx(inputs));
13
- }
14
- var buttonVariants = cva(
15
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-all disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
16
- {
17
- variants: {
18
- variant: {
19
- default: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 active:scale-[0.98]",
20
- destructive: "bg-destructive text-white hover:bg-destructive/90 active:bg-destructive/80 active:scale-[0.98] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 dark:active:bg-destructive/70",
21
- outline: "border bg-background text-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent/80 active:scale-[0.98] dark:bg-input/30 dark:border-input dark:hover:bg-input/50 dark:active:bg-input/60",
22
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70 active:scale-[0.98]",
23
- ghost: "hover:bg-accent hover:text-accent-foreground active:bg-accent/90 active:scale-[0.98] dark:hover:bg-accent/50 dark:active:bg-accent/40",
24
- link: "text-primary underline-offset-4 hover:underline active:text-primary/80"
25
- },
26
- size: {
27
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
28
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
29
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
30
- icon: "size-9 rounded-md"
31
- }
32
- },
33
- defaultVariants: {
34
- variant: "default",
35
- size: "default"
36
- }
37
- }
38
- );
39
- var Button = React.forwardRef(function Button2({ className, variant, size, asChild = false, ...props }, ref) {
40
- const Comp = asChild ? Slot : "button";
41
- return /* @__PURE__ */ jsx(
42
- Comp,
43
- {
44
- ref,
45
- "data-slot": "button",
46
- className: cn(buttonVariants({ variant, size }), className),
47
- ...props
48
- }
49
- );
50
- });
51
- Button.displayName = "Button";
52
7
  function ImagePickerField({
53
8
  label = "Image",
54
9
  value,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ui/utils.ts","../src/ui/button.tsx","../src/files/image-picker-field.tsx"],"names":["Button","jsx"],"mappings":"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","file":"image-picker-field.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","import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"./utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-all disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 active:scale-[0.98]\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 active:bg-destructive/80 active:scale-[0.98] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 dark:active:bg-destructive/70\",\n outline:\n \"border bg-background text-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent/80 active:scale-[0.98] dark:bg-input/30 dark:border-input dark:hover:bg-input/50 dark:active:bg-input/60\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70 active:scale-[0.98]\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground active:bg-accent/90 active:scale-[0.98] dark:hover:bg-accent/50 dark:active:bg-accent/40\",\n link: \"text-primary underline-offset-4 hover:underline active:text-primary/80\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9 rounded-md\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nconst Button = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }\n>(function Button({ className, variant, size, asChild = false, ...props }, ref) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n />\n );\n});\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import { useRef } from \"react\";\nimport { ImagePlus, Trash2, Upload } from \"lucide-react\";\nimport { Button } from \"../ui/button\";\n\nexport function ImagePickerField({\n label = \"Image\",\n value,\n onChange,\n description,\n previewAlt = \"Selected image\",\n}: {\n label?: string;\n value?: string;\n onChange: (value: string) => void;\n description?: string;\n previewAlt?: string;\n}) {\n const inputRef = useRef<HTMLInputElement>(null);\n\n const handleFileChange = (files: FileList | null) => {\n const file = files?.[0];\n if (!file) return;\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === \"string\") onChange(reader.result);\n };\n reader.readAsDataURL(file);\n };\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-xs font-medium text-muted-foreground\">{label}</label>\n {value ? (\n <div className=\"max-w-sm overflow-hidden rounded-xl border border-border bg-card\">\n <img src={value} alt={previewAlt} className=\"aspect-[4/3] w-full object-cover\" />\n <div className=\"flex items-center justify-between gap-2 border-t border-border px-3 py-2\">\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n onClick={() => inputRef.current?.click()}\n className=\"h-auto gap-1.5 p-0 text-xs font-medium text-blue-600 dark:text-blue-400\"\n >\n <ImagePlus size={13} />\n Change image\n </Button>\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n onClick={() => onChange(\"\")}\n className=\"h-auto gap-1.5 p-0 text-xs font-medium text-red-600\"\n >\n <Trash2 size={13} />\n Remove\n </Button>\n </div>\n </div>\n ) : (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => inputRef.current?.click()}\n className=\"flex aspect-[4/3] h-auto max-w-sm w-full flex-col items-center justify-center gap-2 rounded-xl border-dashed bg-muted/50 px-4 py-5 text-center shadow-none hover:border-blue-300 hover:bg-blue-50/30 dark:hover:bg-blue-950/25\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-xl border border-border bg-card\">\n <Upload size={15} className=\"text-muted-foreground\" />\n </span>\n <span className=\"text-xs font-medium text-foreground\">Choose image</span>\n <span className=\"text-[11px] font-normal text-muted-foreground whitespace-normal\">\n {description ?? \"JPG, PNG or WebP. Stored in this prototype only.\"}\n </span>\n </Button>\n )}\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/*\"\n className=\"hidden\"\n onChange={(event) => {\n handleFileChange(event.target.files);\n event.target.value = \"\";\n }}\n />\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/files/image-picker-field.tsx"],"names":[],"mappings":"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","file":"image-picker-field.js","sourcesContent":["import { useRef } from \"react\";\nimport { ImagePlus, Trash2, Upload } from \"lucide-react\";\nimport { Button } from \"../ui/button\";\n\nexport function ImagePickerField({\n label = \"Image\",\n value,\n onChange,\n description,\n previewAlt = \"Selected image\",\n}: {\n label?: string;\n value?: string;\n onChange: (value: string) => void;\n description?: string;\n previewAlt?: string;\n}) {\n const inputRef = useRef<HTMLInputElement>(null);\n\n const handleFileChange = (files: FileList | null) => {\n const file = files?.[0];\n if (!file) return;\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === \"string\") onChange(reader.result);\n };\n reader.readAsDataURL(file);\n };\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-xs font-medium text-muted-foreground\">{label}</label>\n {value ? (\n <div className=\"max-w-sm overflow-hidden rounded-xl border border-border bg-card\">\n <img src={value} alt={previewAlt} className=\"aspect-[4/3] w-full object-cover\" />\n <div className=\"flex items-center justify-between gap-2 border-t border-border px-3 py-2\">\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n onClick={() => inputRef.current?.click()}\n className=\"h-auto gap-1.5 p-0 text-xs font-medium text-blue-600 dark:text-blue-400\"\n >\n <ImagePlus size={13} />\n Change image\n </Button>\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n onClick={() => onChange(\"\")}\n className=\"h-auto gap-1.5 p-0 text-xs font-medium text-red-600\"\n >\n <Trash2 size={13} />\n Remove\n </Button>\n </div>\n </div>\n ) : (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => inputRef.current?.click()}\n className=\"flex aspect-[4/3] h-auto max-w-sm w-full flex-col items-center justify-center gap-2 rounded-xl border-dashed bg-muted/50 px-4 py-5 text-center shadow-none hover:border-blue-300 hover:bg-blue-50/30 dark:hover:bg-blue-950/25\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-xl border border-border bg-card\">\n <Upload size={15} className=\"text-muted-foreground\" />\n </span>\n <span className=\"text-xs font-medium text-foreground\">Choose image</span>\n <span className=\"text-[11px] font-normal text-muted-foreground whitespace-normal\">\n {description ?? \"JPG, PNG or WebP. Stored in this prototype only.\"}\n </span>\n </Button>\n )}\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/*\"\n className=\"hidden\"\n onChange={(event) => {\n handleFileChange(event.target.files);\n event.target.value = \"\";\n }}\n />\n </div>\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
 
3
3
  type LayoutCanvasResourceType = "desks" | "rooms" | "other";
4
4
  interface LayoutCanvasRect {
@@ -52,6 +52,6 @@ interface LayoutCanvasViewProps {
52
52
  searchPlaceholder?: string;
53
53
  }
54
54
  declare function matchesCanvasResourceSearch(resourceName: string, planName: string | undefined, query: string): boolean;
55
- declare function LayoutCanvasView({ layout, rows, onResourceSelect, resourceQuickBook, searchQuery, onSearchQueryChange, searchPlaceholder, }: LayoutCanvasViewProps): react_jsx_runtime.JSX.Element;
55
+ declare function LayoutCanvasView({ layout, rows, onResourceSelect, resourceQuickBook, searchQuery, onSearchQueryChange, searchPlaceholder, }: LayoutCanvasViewProps): React.JSX.Element;
56
56
 
57
57
  export { type LayoutCanvasResourceQuickBook, LayoutCanvasView, type LayoutCanvasViewProps, type LayoutCanvasViewRow, matchesCanvasResourceSearch };
@@ -1,228 +1,25 @@
1
- import * as React from 'react';
1
+ import { useTheme } from './chunk-XU3EFSQX.js';
2
+ import './chunk-4Y5DBM6Z.js';
3
+ import { LIVE_RESOURCE_TYPE_META } from './chunk-ZIJQ7BXU.js';
4
+ import { Tooltip, TooltipTrigger, TooltipContent, Popover, PopoverAnchor, PopoverContent } from './chunk-4A6RCBB5.js';
5
+ import './chunk-RV5PMZYZ.js';
6
+ import './chunk-L65F4EMK.js';
7
+ import './chunk-PBNBLCC2.js';
8
+ import './chunk-VVXGQKN7.js';
9
+ import './chunk-7YCAAP43.js';
10
+ import { PageListSearchField } from './chunk-H7EX6B6N.js';
11
+ import './chunk-LUB7QWGU.js';
12
+ import { Button } from './chunk-VHINHHZQ.js';
13
+ import './chunk-FCY5FYJB.js';
14
+ import './chunk-OWNUMPUC.js';
15
+ import { cn } from './chunk-DMPSQ22Q.js';
2
16
  import { useMemo, useState, useRef, useCallback, useEffect } from 'react';
3
- import { Minus, Plus, Maximize2, Box, DoorOpen, Armchair, Search, Calendar } from 'lucide-react';
4
- import { clsx } from 'clsx';
5
- import { twMerge } from 'tailwind-merge';
6
- import { Slot } from '@radix-ui/react-slot';
7
- import { cva } from 'class-variance-authority';
8
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
- import * as PopoverPrimitive from '@radix-ui/react-popover';
10
- import * as TooltipPrimitive from '@radix-ui/react-tooltip';
11
- import { useTheme } from 'next-themes';
12
-
13
- // src/live-space/layout-canvas-view.tsx
17
+ import { Minus, Plus, Maximize2, Box, DoorOpen, Armchair, Calendar } from 'lucide-react';
18
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
14
19
 
15
20
  // src/live-space/layout-canvas-types.ts
16
21
  var DEFAULT_CANVAS_WIDTH = 1200;
17
22
  var DEFAULT_CANVAS_HEIGHT = 900;
18
- var LIVE_RESOURCE_TYPE_META = {
19
- desks: {
20
- label: "Desk",
21
- rowAccent: "text-blue-700 bg-blue-50 border-blue-100",
22
- Icon: Armchair,
23
- iconTile: "bg-blue-50 text-blue-600 border-blue-100 dark:bg-blue-950/50 dark:text-blue-400 dark:border-blue-900/50"
24
- },
25
- rooms: {
26
- label: "Room",
27
- rowAccent: "text-orange-700 bg-orange-50 border-orange-100",
28
- Icon: DoorOpen,
29
- iconTile: "bg-orange-50 text-orange-600 border-orange-100 dark:bg-orange-950/50 dark:text-orange-400 dark:border-orange-900/50"
30
- },
31
- other: {
32
- label: "Other",
33
- rowAccent: "text-purple-700 bg-purple-50 border-purple-100",
34
- Icon: Box,
35
- iconTile: "bg-purple-50 text-purple-600 border-purple-100 dark:bg-purple-950/50 dark:text-purple-400 dark:border-purple-900/50"
36
- }
37
- };
38
- function cn(...inputs) {
39
- return twMerge(clsx(inputs));
40
- }
41
- var buttonVariants = cva(
42
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-all disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
43
- {
44
- variants: {
45
- variant: {
46
- default: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 active:scale-[0.98]",
47
- destructive: "bg-destructive text-white hover:bg-destructive/90 active:bg-destructive/80 active:scale-[0.98] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 dark:active:bg-destructive/70",
48
- outline: "border bg-background text-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent/80 active:scale-[0.98] dark:bg-input/30 dark:border-input dark:hover:bg-input/50 dark:active:bg-input/60",
49
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70 active:scale-[0.98]",
50
- ghost: "hover:bg-accent hover:text-accent-foreground active:bg-accent/90 active:scale-[0.98] dark:hover:bg-accent/50 dark:active:bg-accent/40",
51
- link: "text-primary underline-offset-4 hover:underline active:text-primary/80"
52
- },
53
- size: {
54
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
55
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
56
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
57
- icon: "size-9 rounded-md"
58
- }
59
- },
60
- defaultVariants: {
61
- variant: "default",
62
- size: "default"
63
- }
64
- }
65
- );
66
- var Button = React.forwardRef(function Button2({ className, variant, size, asChild = false, ...props }, ref) {
67
- const Comp = asChild ? Slot : "button";
68
- return /* @__PURE__ */ jsx(
69
- Comp,
70
- {
71
- ref,
72
- "data-slot": "button",
73
- className: cn(buttonVariants({ variant, size }), className),
74
- ...props
75
- }
76
- );
77
- });
78
- Button.displayName = "Button";
79
- var inputVariants = cva(
80
- "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-border bg-card text-foreground flex w-full min-w-0 rounded-md border px-3 py-1 text-base transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:border-blue-400 focus-visible:ring-[3px] focus-visible:ring-blue-500/25 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-red-400 aria-invalid:ring-red-500/20 md:text-sm",
81
- {
82
- variants: {
83
- size: {
84
- sm: "h-8 text-sm",
85
- default: "h-9",
86
- lg: "h-10",
87
- xl: "h-11"
88
- }
89
- },
90
- defaultVariants: {
91
- size: "default"
92
- }
93
- }
94
- );
95
- var Input = React.forwardRef(function Input2({ className, type, size, ...props }, ref) {
96
- return /* @__PURE__ */ jsx(
97
- "input",
98
- {
99
- ref,
100
- type,
101
- "data-slot": "input",
102
- className: cn(inputVariants({ size }), className),
103
- ...props
104
- }
105
- );
106
- });
107
- function SearchField({
108
- value,
109
- onChange,
110
- placeholder,
111
- className,
112
- size = "default",
113
- "aria-label": ariaLabel
114
- }) {
115
- const heightClass = size === "lg" ? "h-11" : "h-10";
116
- const iconSize = size === "lg" ? 16 : 16;
117
- return /* @__PURE__ */ jsxs(
118
- "div",
119
- {
120
- className: cn(
121
- "flex items-center gap-2 rounded-md border border-border bg-card px-3 w-full focus-within:border-blue-400 focus-within:ring-[3px] focus-within:ring-blue-500/25 transition-all",
122
- heightClass,
123
- className
124
- ),
125
- children: [
126
- /* @__PURE__ */ jsx(Search, { size: iconSize, className: "text-muted-foreground shrink-0", strokeWidth: 2.25 }),
127
- /* @__PURE__ */ jsx(
128
- Input,
129
- {
130
- type: "search",
131
- value,
132
- onChange: (e) => onChange(e.target.value),
133
- placeholder,
134
- "aria-label": ariaLabel,
135
- className: "flex-1 border-0 bg-transparent shadow-none focus-visible:ring-0 px-0 min-w-0 h-auto"
136
- }
137
- )
138
- ]
139
- }
140
- );
141
- }
142
- function PageListSearchField({
143
- value,
144
- onChange,
145
- placeholder,
146
- className
147
- }) {
148
- return /* @__PURE__ */ jsx(
149
- SearchField,
150
- {
151
- value,
152
- onChange,
153
- placeholder,
154
- className
155
- }
156
- );
157
- }
158
- function Popover({ ...props }) {
159
- return /* @__PURE__ */ jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
160
- }
161
- function PopoverContent({
162
- className,
163
- align = "center",
164
- sideOffset = 4,
165
- ...props
166
- }) {
167
- return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
168
- PopoverPrimitive.Content,
169
- {
170
- "data-slot": "popover-content",
171
- align,
172
- sideOffset,
173
- className: cn(
174
- "bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border border-white/10 p-4 shadow-md outline-hidden",
175
- className
176
- ),
177
- ...props
178
- }
179
- ) });
180
- }
181
- function PopoverAnchor({ ...props }) {
182
- return /* @__PURE__ */ jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
183
- }
184
- function TooltipProvider({
185
- delayDuration = 0,
186
- ...props
187
- }) {
188
- return /* @__PURE__ */ jsx(
189
- TooltipPrimitive.Provider,
190
- {
191
- "data-slot": "tooltip-provider",
192
- delayDuration,
193
- ...props
194
- }
195
- );
196
- }
197
- function Tooltip({ ...props }) {
198
- return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) });
199
- }
200
- function TooltipTrigger({ ...props }) {
201
- return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
202
- }
203
- function TooltipContent({
204
- className,
205
- sideOffset = 0,
206
- children,
207
- ...props
208
- }) {
209
- return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
210
- TooltipPrimitive.Content,
211
- {
212
- "data-slot": "tooltip-content",
213
- sideOffset,
214
- className: cn(
215
- "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
216
- className
217
- ),
218
- ...props,
219
- children: [
220
- children,
221
- /* @__PURE__ */ jsx(TooltipPrimitive.Arrow, { className: "bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
222
- ]
223
- }
224
- ) });
225
- }
226
23
  function matchesCanvasResourceSearch(resourceName, planName, query) {
227
24
  const q = query.trim().toLowerCase();
228
25
  if (!q) return true;