opacacms 0.1.12 → 0.1.13

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 (217) hide show
  1. package/dist/admin/index.js +0 -4
  2. package/dist/admin/webcomponent.d.ts +0 -1
  3. package/dist/admin/webcomponent.js +0 -4
  4. package/dist/admin.css +1 -0
  5. package/package.json +8 -2
  6. package/bun.lock +0 -34
  7. package/dist/admin/index.css +0 -47
  8. package/dist/admin/webcomponent.css +0 -47
  9. package/global.d.ts +0 -11
  10. package/src/admin/api-client.ts +0 -63
  11. package/src/admin/auth-client.ts +0 -40
  12. package/src/admin/custom-field.ts +0 -179
  13. package/src/admin/index.ts +0 -15
  14. package/src/admin/react.tsx +0 -72
  15. package/src/admin/router.ts +0 -9
  16. package/src/admin/stores/admin-queries.ts +0 -121
  17. package/src/admin/stores/auth.ts +0 -61
  18. package/src/admin/stores/column-visibility.ts +0 -67
  19. package/src/admin/stores/config.ts +0 -15
  20. package/src/admin/stores/media.ts +0 -95
  21. package/src/admin/stores/query.ts +0 -13
  22. package/src/admin/stores/ui.ts +0 -29
  23. package/src/admin/ui/admin-client.tsx +0 -283
  24. package/src/admin/ui/admin-layout.tsx +0 -276
  25. package/src/admin/ui/components/ColumnVisibilityToggle.tsx +0 -141
  26. package/src/admin/ui/components/DataDetailSheet.tsx +0 -141
  27. package/src/admin/ui/components/DataDetailView.tsx +0 -175
  28. package/src/admin/ui/components/Table.tsx +0 -67
  29. package/src/admin/ui/components/fields/ArrayField.tsx +0 -166
  30. package/src/admin/ui/components/fields/BlocksField.tsx +0 -202
  31. package/src/admin/ui/components/fields/BooleanField.tsx +0 -50
  32. package/src/admin/ui/components/fields/CollapsibleField.tsx +0 -75
  33. package/src/admin/ui/components/fields/DateField.tsx +0 -45
  34. package/src/admin/ui/components/fields/FileField.tsx +0 -322
  35. package/src/admin/ui/components/fields/GroupField.tsx +0 -50
  36. package/src/admin/ui/components/fields/JoinField.tsx +0 -23
  37. package/src/admin/ui/components/fields/NumberField.tsx +0 -46
  38. package/src/admin/ui/components/fields/RadioField.tsx +0 -62
  39. package/src/admin/ui/components/fields/RelationshipField.tsx +0 -278
  40. package/src/admin/ui/components/fields/RowField.tsx +0 -40
  41. package/src/admin/ui/components/fields/SelectField.tsx +0 -59
  42. package/src/admin/ui/components/fields/TabsField.tsx +0 -101
  43. package/src/admin/ui/components/fields/TextAreaField.tsx +0 -54
  44. package/src/admin/ui/components/fields/TextField.tsx +0 -49
  45. package/src/admin/ui/components/fields/VirtualField.tsx +0 -53
  46. package/src/admin/ui/components/fields/index.tsx +0 -371
  47. package/src/admin/ui/components/fields/richtext-editor/index.tsx +0 -211
  48. package/src/admin/ui/components/fields/richtext-editor/nodes/ImageComponent.tsx +0 -142
  49. package/src/admin/ui/components/fields/richtext-editor/nodes/ImageNode.tsx +0 -95
  50. package/src/admin/ui/components/fields/richtext-editor/plugins/ComponentPickerPlugin.tsx +0 -226
  51. package/src/admin/ui/components/fields/richtext-editor/plugins/EditableSyncPlugin.tsx +0 -16
  52. package/src/admin/ui/components/fields/richtext-editor/plugins/NotionToolbarPlugin.tsx +0 -184
  53. package/src/admin/ui/components/fields/richtext-editor/plugins/SimpleToolbarPlugin.tsx +0 -240
  54. package/src/admin/ui/components/fields/richtext-editor/plugins/ValueSyncPlugin.tsx +0 -40
  55. package/src/admin/ui/components/fields/utils.ts +0 -1
  56. package/src/admin/ui/components/link.tsx +0 -41
  57. package/src/admin/ui/components/media/AssetManagerModal.tsx +0 -334
  58. package/src/admin/ui/components/toast.tsx +0 -72
  59. package/src/admin/ui/components/ui/accordion.tsx +0 -51
  60. package/src/admin/ui/components/ui/alert-dialog.tsx +0 -98
  61. package/src/admin/ui/components/ui/blocks.tsx +0 -32
  62. package/src/admin/ui/components/ui/breadcrumbs.tsx +0 -59
  63. package/src/admin/ui/components/ui/button.tsx +0 -26
  64. package/src/admin/ui/components/ui/collapsible.tsx +0 -124
  65. package/src/admin/ui/components/ui/dialog.tsx +0 -79
  66. package/src/admin/ui/components/ui/group.tsx +0 -20
  67. package/src/admin/ui/components/ui/index.ts +0 -17
  68. package/src/admin/ui/components/ui/input.tsx +0 -12
  69. package/src/admin/ui/components/ui/join.tsx +0 -53
  70. package/src/admin/ui/components/ui/label.tsx +0 -11
  71. package/src/admin/ui/components/ui/radio-group.tsx +0 -75
  72. package/src/admin/ui/components/ui/relationship-detail-sheet.tsx +0 -122
  73. package/src/admin/ui/components/ui/relationship.tsx +0 -58
  74. package/src/admin/ui/components/ui/scroll-area.tsx +0 -19
  75. package/src/admin/ui/components/ui/select.tsx +0 -187
  76. package/src/admin/ui/components/ui/separator.tsx +0 -21
  77. package/src/admin/ui/components/ui/sheet.tsx +0 -106
  78. package/src/admin/ui/components/ui/tabs.tsx +0 -116
  79. package/src/admin/ui/components/ui/utils.ts +0 -3
  80. package/src/admin/ui/hooks/use-debounce.ts +0 -15
  81. package/src/admin/ui/styles/_locale-switcher.scss +0 -33
  82. package/src/admin/ui/styles/accordion.scss +0 -60
  83. package/src/admin/ui/styles/animations.scss +0 -41
  84. package/src/admin/ui/styles/asset-manager.scss +0 -547
  85. package/src/admin/ui/styles/badge.scss +0 -13
  86. package/src/admin/ui/styles/base.scss +0 -22
  87. package/src/admin/ui/styles/button.scss +0 -161
  88. package/src/admin/ui/styles/card.scss +0 -13
  89. package/src/admin/ui/styles/collapsible.scss +0 -75
  90. package/src/admin/ui/styles/data-detail.scss +0 -92
  91. package/src/admin/ui/styles/dialog.scss +0 -102
  92. package/src/admin/ui/styles/empty-state.scss +0 -22
  93. package/src/admin/ui/styles/group.scss +0 -19
  94. package/src/admin/ui/styles/index.scss +0 -33
  95. package/src/admin/ui/styles/input.scss +0 -80
  96. package/src/admin/ui/styles/label.scss +0 -12
  97. package/src/admin/ui/styles/layout.scss +0 -56
  98. package/src/admin/ui/styles/lexical.scss +0 -469
  99. package/src/admin/ui/styles/loading.scss +0 -102
  100. package/src/admin/ui/styles/media-registry.scss +0 -597
  101. package/src/admin/ui/styles/pagination.scss +0 -20
  102. package/src/admin/ui/styles/radio-group.scss +0 -66
  103. package/src/admin/ui/styles/row.scss +0 -17
  104. package/src/admin/ui/styles/scrollbar.scss +0 -36
  105. package/src/admin/ui/styles/select.scss +0 -121
  106. package/src/admin/ui/styles/separator.scss +0 -14
  107. package/src/admin/ui/styles/sheet.scss +0 -152
  108. package/src/admin/ui/styles/sidebar.scss +0 -148
  109. package/src/admin/ui/styles/switch.scss +0 -59
  110. package/src/admin/ui/styles/table.scss +0 -207
  111. package/src/admin/ui/styles/tabs.scss +0 -62
  112. package/src/admin/ui/styles/toast.scss +0 -45
  113. package/src/admin/ui/styles/variables.scss +0 -24
  114. package/src/admin/ui/views/collection-list-view.tsx +0 -720
  115. package/src/admin/ui/views/dashboard-view.tsx +0 -263
  116. package/src/admin/ui/views/document-edit-view.tsx +0 -384
  117. package/src/admin/ui/views/global-edit-view.tsx +0 -226
  118. package/src/admin/ui/views/init-view.tsx +0 -182
  119. package/src/admin/ui/views/login-view.tsx +0 -123
  120. package/src/admin/ui/views/media-registry-view.tsx +0 -1104
  121. package/src/admin/ui/views/settings-view.tsx +0 -729
  122. package/src/admin/webcomponent.tsx +0 -15
  123. package/src/auth/index.ts +0 -194
  124. package/src/auth/migrations.ts +0 -87
  125. package/src/auth/premissions.ts +0 -46
  126. package/src/cli/commands/generate-types.ts +0 -116
  127. package/src/cli/commands/init.ts +0 -95
  128. package/src/cli/commands/migrate-commands.ts +0 -160
  129. package/src/cli/commands/seed-command.ts +0 -11
  130. package/src/cli/d1-mock.ts +0 -101
  131. package/src/cli/index.test.ts +0 -84
  132. package/src/cli/index.ts +0 -183
  133. package/src/cli/r2-mock.ts +0 -217
  134. package/src/cli/seeding.ts +0 -409
  135. package/src/client.ts +0 -181
  136. package/src/config-utils.ts +0 -102
  137. package/src/config.ts +0 -49
  138. package/src/db/adapter.ts +0 -53
  139. package/src/db/better-sqlite.ts +0 -657
  140. package/src/db/bun-sqlite.ts +0 -666
  141. package/src/db/d1.ts +0 -721
  142. package/src/db/index.ts +0 -10
  143. package/src/db/kysely/data-mapper.ts +0 -142
  144. package/src/db/kysely/field-mapper.ts +0 -149
  145. package/src/db/kysely/migration-generator.ts +0 -223
  146. package/src/db/kysely/query-builder.ts +0 -92
  147. package/src/db/kysely/schema-builder.ts +0 -439
  148. package/src/db/kysely/sql-utils.ts +0 -13
  149. package/src/db/postgres.ts +0 -631
  150. package/src/db/sqlite.ts +0 -670
  151. package/src/db/system-schema.ts +0 -121
  152. package/src/index.ts +0 -13
  153. package/src/runtimes/README.md +0 -59
  154. package/src/runtimes/bun.ts +0 -49
  155. package/src/runtimes/cloudflare-workers.ts +0 -38
  156. package/src/runtimes/next.ts +0 -26
  157. package/src/runtimes/node.ts +0 -52
  158. package/src/schema/collection.ts +0 -184
  159. package/src/schema/fields/base.ts +0 -164
  160. package/src/schema/fields/index.ts +0 -427
  161. package/src/schema/global.ts +0 -145
  162. package/src/schema/index.ts +0 -4
  163. package/src/schema/infer.ts +0 -72
  164. package/src/server/admin-router.ts +0 -20
  165. package/src/server/admin.ts +0 -142
  166. package/src/server/assets.ts +0 -306
  167. package/src/server/collection-router.ts +0 -55
  168. package/src/server/handlers.ts +0 -722
  169. package/src/server/middlewares/admin.ts +0 -27
  170. package/src/server/middlewares/auth.ts +0 -89
  171. package/src/server/middlewares/context.ts +0 -17
  172. package/src/server/middlewares/cors.ts +0 -24
  173. package/src/server/middlewares/database-init.ts +0 -74
  174. package/src/server/middlewares/rate-limit.ts +0 -77
  175. package/src/server/router.ts +0 -47
  176. package/src/server/setup-middlewares.ts +0 -58
  177. package/src/server/system-router.ts +0 -35
  178. package/src/server.ts +0 -9
  179. package/src/storage/adapters/cloudflare-r2.ts +0 -136
  180. package/src/storage/adapters/local.ts +0 -146
  181. package/src/storage/adapters/s3.ts +0 -186
  182. package/src/storage/errors.ts +0 -46
  183. package/src/storage/index.ts +0 -5
  184. package/src/storage/types.ts +0 -39
  185. package/src/types.ts +0 -577
  186. package/src/utils/lexical.ts +0 -37
  187. package/src/utils/logger.ts +0 -73
  188. package/src/validation.ts +0 -429
  189. package/src/validator.ts +0 -179
  190. package/test/admin-custom-field.test.ts +0 -162
  191. package/test/admin-react-field.test.tsx +0 -134
  192. package/test/api-features.test.ts +0 -78
  193. package/test/api.test.ts +0 -178
  194. package/test/auth.test.ts +0 -62
  195. package/test/cli-integration.test.ts +0 -148
  196. package/test/cli.test.ts +0 -25
  197. package/test/db/postgres.test.ts +0 -95
  198. package/test/db/sqlite-filter.test.ts +0 -53
  199. package/test/db/sqlite.test.ts +0 -82
  200. package/test/engine-features.test.ts +0 -79
  201. package/test/globals.test.ts +0 -74
  202. package/test/integration-tmp/db-app/opacacms.config.ts +0 -15
  203. package/test/integration-tmp/my-sqlite-app/opacacms.config.ts +0 -25
  204. package/test/integration-tmp/my-test-app/index.ts +0 -8
  205. package/test/integration-tmp/my-test-app/opacacms.config.ts +0 -16
  206. package/test/integration-tmp/my-test-app/package.json +0 -12
  207. package/test/populate.test.ts +0 -79
  208. package/test/runtimes.test.ts +0 -43
  209. package/test/schema-builder.test.ts +0 -107
  210. package/test/schema-features.test.ts +0 -63
  211. package/test/seeding.test.ts +0 -68
  212. package/test/storage/local.test.ts +0 -72
  213. package/test/storage/s3.test.ts +0 -60
  214. package/test/structural-data.test.ts +0 -100
  215. package/test/test-setup.ts +0 -11
  216. package/test/validation.test.ts +0 -162
  217. package/tsconfig.json +0 -42
@@ -274,7 +274,6 @@ import * as React2 from "react";
274
274
  function cn(...classes) {
275
275
  return classes.filter(Boolean).join(" ");
276
276
  }
277
-
278
277
  // src/admin/ui/components/ui/accordion.tsx
279
278
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
280
279
  var Accordion = ({
@@ -2016,7 +2015,6 @@ var setBucketColor = (bucket, color) => {
2016
2015
  const current = $bucketColors.get();
2017
2016
  $bucketColors.set({ ...current, [bucket]: color });
2018
2017
  };
2019
-
2020
2018
  // src/admin/ui/components/media/AssetManagerModal.tsx
2021
2019
  import { jsxDEV as jsxDEV24, Fragment as Fragment2 } from "react/jsx-dev-runtime";
2022
2020
  var AssetManagerModal = ({
@@ -2977,7 +2975,6 @@ function ValueSyncPlugin({ value }) {
2977
2975
  }, [editor, value]);
2978
2976
  return null;
2979
2977
  }
2980
-
2981
2978
  // src/admin/ui/components/fields/richtext-editor/index.tsx
2982
2979
  import { jsxDEV as jsxDEV28 } from "react/jsx-dev-runtime";
2983
2980
  var theme = {
@@ -3315,7 +3312,6 @@ var DataDetailView = ({
3315
3312
  children: String(data)
3316
3313
  }, undefined, false, undefined, this);
3317
3314
  };
3318
-
3319
3315
  // src/admin/ui/components/DataDetailSheet.tsx
3320
3316
  import { useStore as useStore4 } from "@nanostores/react";
3321
3317
  import { jsxDEV as jsxDEV30, Fragment as Fragment6 } from "react/jsx-dev-runtime";
@@ -1,2 +1 @@
1
- import './ui/styles/index.scss';
2
1
  export declare const OpacaAdminWebComponent: CustomElementConstructor;
@@ -278,7 +278,6 @@ import * as React from "react";
278
278
  function cn(...classes) {
279
279
  return classes.filter(Boolean).join(" ");
280
280
  }
281
-
282
281
  // src/admin/ui/components/ui/accordion.tsx
283
282
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
284
283
  var Accordion = ({
@@ -2038,7 +2037,6 @@ var setBucketColor = (bucket, color) => {
2038
2037
  const current = $bucketColors.get();
2039
2038
  $bucketColors.set({ ...current, [bucket]: color });
2040
2039
  };
2041
-
2042
2040
  // src/admin/ui/components/media/AssetManagerModal.tsx
2043
2041
  import { jsxDEV as jsxDEV24, Fragment as Fragment2 } from "react/jsx-dev-runtime";
2044
2042
  var AssetManagerModal = ({
@@ -2999,7 +2997,6 @@ function ValueSyncPlugin({ value }) {
2999
2997
  }, [editor, value]);
3000
2998
  return null;
3001
2999
  }
3002
-
3003
3000
  // src/admin/ui/components/fields/richtext-editor/index.tsx
3004
3001
  import { jsxDEV as jsxDEV28 } from "react/jsx-dev-runtime";
3005
3002
  var theme = {
@@ -3337,7 +3334,6 @@ var DataDetailView = ({
3337
3334
  children: String(data)
3338
3335
  }, undefined, false, undefined, this);
3339
3336
  };
3340
-
3341
3337
  // src/admin/ui/components/DataDetailSheet.tsx
3342
3338
  import { useStore as useStore4 } from "@nanostores/react";
3343
3339
  import { jsxDEV as jsxDEV30, Fragment as Fragment6 } from "react/jsx-dev-runtime";
package/dist/admin.css ADDED
@@ -0,0 +1 @@
1
+ :root{--opaca-bg: #09090b;--opaca-sidebar-bg: #0c0c0f;--opaca-card-bg: #111114;--opaca-surface: #16161a;--opaca-border: #1e1e24;--opaca-border-hover: #2a2a32;--opaca-text: #e4e4e7;--opaca-text-muted: #71717a;--opaca-text-dim: #52525b;--opaca-primary: #7c3aed;--opaca-primary-hover: #6d28d9;--opaca-primary-glow: rgba(124, 58, 237, 0.15);--opaca-accent: #a78bfa;--opaca-error: #f87171;--opaca-error-bg: rgba(248, 113, 113, 0.08);--opaca-success: #34d399;--opaca-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;--opaca-radius: 4px;--opaca-radius-lg: 6px;--opaca-transition: 200ms cubic-bezier(0.4, 0, 0.2, 1);--opaca-sidebar-width: 240px;--opaca-sidebar-collapsed-width: 68px}@keyframes opaca-fade-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes opaca-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes opaca-toast-in{from{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes opaca-pulse{0%,100%{transform:scale(1);opacity:.2}50%{transform:scale(1.4);opacity:0}}.opaca-spin{animation:opaca-spin 1s linear infinite}.opaca-admin *,.opaca-admin *::before,.opaca-admin *::after{box-sizing:border-box;margin:0;padding:0}.opaca-admin{background-color:var(--opaca-bg);color:var(--opaca-text);font-family:var(--opaca-font);height:100vh;display:flex;font-size:14px;line-height:1.6;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overflow:hidden}.opaca-admin ::-webkit-scrollbar{width:6px;height:6px}.opaca-admin ::-webkit-scrollbar-track{background:rgba(0,0,0,0)}.opaca-admin ::-webkit-scrollbar-thumb{background:var(--opaca-border);border-radius:10px}.opaca-admin ::-webkit-scrollbar-thumb:hover{background:var(--opaca-border-hover)}.opaca-scroll-area::-webkit-scrollbar{width:4px}.opaca-scroll-area::-webkit-scrollbar-thumb{background:var(--opaca-border);border-radius:10px}.opaca-scroll-area::-webkit-scrollbar-thumb:hover{background:var(--opaca-accent)}.opaca-sidebar{width:var(--opaca-sidebar-width);background-color:var(--opaca-sidebar-bg);border-right:1px solid var(--opaca-border);height:100%;display:flex;flex-direction:column;transition:width var(--opaca-transition),padding var(--opaca-transition);z-index:100;overflow:visible;backdrop-filter:blur(10px)}.opaca-sidebar.collapsed{width:var(--opaca-sidebar-collapsed-width)}.opaca-sidebar.collapsed .opaca-sidebar-inner{padding-left:.5rem;padding-right:.5rem}.opaca-sidebar.collapsed .opaca-nav-item{padding-left:0;padding-right:0;justify-content:center;gap:0}.opaca-sidebar.collapsed .opaca-nav-label{opacity:0;width:0}.opaca-sidebar.collapsed .opaca-sidebar-toggle{right:-12px}.opaca-sidebar-inner{display:flex;flex-direction:column;height:100%;overflow:hidden;padding:1.25rem .75rem}.opaca-logo{font-size:.875rem;font-weight:600;letter-spacing:-0.01em;padding:0 .75rem;margin-bottom:2rem;color:var(--opaca-accent);transition:all var(--opaca-transition);white-space:nowrap;display:flex;align-items:center;min-height:32px}.opaca-logo-mini{width:32px;height:32px;background-color:rgba(124,58,237,.1);border:1px solid rgba(124,58,237,.2);color:var(--opaca-accent);display:flex;align-items:center;justify-content:center;border-radius:var(--opaca-radius);font-size:.875rem;font-weight:700}.opaca-nav{flex:1;display:flex;flex-direction:column;min-height:0}.opaca-nav-item{display:flex;align-items:center;gap:.625rem;padding:.5rem .75rem;color:var(--opaca-text-muted);text-decoration:none;border-radius:var(--opaca-radius);transition:all var(--opaca-transition);margin-bottom:1px;font-size:.8125rem;font-weight:400;white-space:nowrap;overflow:hidden;border:1px solid rgba(0,0,0,0)}.opaca-nav-item:hover{background-color:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.05);color:var(--opaca-text)}.opaca-nav-item.active{background-color:var(--opaca-primary-glow);color:var(--opaca-accent);font-weight:600;box-shadow:inset 2px 0 0 0 var(--opaca-primary)}.opaca-nav-label{transition:opacity var(--opaca-transition)}.opaca-nav-footer{border-top:1px solid var(--opaca-border);padding-top:.75rem;margin-top:auto;padding-bottom:.5rem}.opaca-sidebar-toggle{position:absolute;top:25px;right:-12px;width:24px;height:24px;background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:50%;color:var(--opaca-text-dim);display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:101;transition:all var(--opaca-transition);box-shadow:0 2px 4px rgba(0,0,0,.1)}.opaca-sidebar-toggle:hover{border-color:var(--opaca-accent);color:var(--opaca-accent);background-color:var(--opaca-surface)}.opaca-content{flex:1;padding:2.5rem;overflow-y:auto;height:100%;scroll-behavior:smooth}.opaca-content-inner{margin:0 auto;width:100%}.opaca-view-container{opacity:1;transform:translateY(0);transition:opacity var(--opaca-transition),transform var(--opaca-transition)}.opaca-view-container.loading{opacity:.5;pointer-events:none;filter:blur(1px)}.opaca-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:2rem}.opaca-title{font-size:1.375rem;font-weight:600;letter-spacing:-0.02em;color:var(--opaca-text)}.opaca-subtitle{color:var(--opaca-text-muted);font-size:.8125rem;margin-top:.25rem}.opaca-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(260px, 1fr));gap:1rem}.opaca-form-group{margin-bottom:1.5rem}.opaca-card{background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);padding:1.25rem;transition:border-color var(--opaca-transition)}.opaca-card:hover{border-color:var(--opaca-border-hover)}.opaca-table-container{background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);overflow:hidden;position:relative}.opaca-loading-overlay{position:absolute;top:0;left:0;right:0;bottom:0;background-color:rgba(9,9,11,.6);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:10;animation:opaca-fade-in 200ms ease-out}.opaca-table{width:100%;border-collapse:collapse}.opaca-table th{text-align:left;padding:.625rem 1rem;background-color:var(--opaca-surface);color:var(--opaca-text-dim);font-weight:500;font-size:.6875rem;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--opaca-border)}.opaca-table td{padding:.625rem 1rem;border-bottom:1px solid var(--opaca-border);font-size:.8125rem;color:var(--opaca-text)}.opaca-table tr:last-child td{border-bottom:none}.opaca-table tr:hover td{background-color:hsla(0,0%,100%,.01)}.opaca-table-wrapper{position:relative;width:100%;overflow:auto;border-radius:var(--opaca-radius-lg);border:1px solid var(--opaca-border);background:var(--opaca-card-bg);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06)}.opaca-table-wrapper::-webkit-scrollbar{height:8px;width:8px}.opaca-table-wrapper::-webkit-scrollbar-track{background:rgba(0,0,0,0)}.opaca-table-wrapper::-webkit-scrollbar-thumb{background:var(--opaca-border);border-radius:10px}.opaca-table-wrapper::-webkit-scrollbar-thumb:hover{background:var(--opaca-text-dim)}.opaca-new-table{width:100%;caption-side:bottom;font-size:.875rem;border-collapse:collapse}.opaca-new-table .opaca-badge{padding:.2rem .5rem;font-size:.7rem;border-radius:4px;background:var(--opaca-surface);color:var(--opaca-text-muted);border:1px solid var(--opaca-border)}.opaca-new-table .opaca-btn-outline{background:rgba(0,0,0,0);border-color:var(--opaca-border);color:var(--opaca-text-muted)}.opaca-new-table .opaca-btn-outline:hover{border-color:var(--opaca-primary);color:var(--opaca-primary)}.opaca-table-header{background-color:hsla(0,0%,100%,.02);border-bottom:1px solid var(--opaca-border)}.opaca-table-head{position:sticky;top:0;z-index:10;background-color:var(--opaca-card-bg);height:3.5rem;padding:0 1rem;text-align:left;vertical-align:middle;font-weight:600;color:var(--opaca-text-muted);font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;min-width:120px;transition:color .2s ease;box-shadow:inset 0 -1px 0 var(--opaca-border)}.opaca-table-head:hover{color:var(--opaca-text)}.opaca-table-head>div{display:flex;align-items:center;white-space:nowrap;gap:.5rem}.opaca-table-row{border-bottom:1px solid var(--opaca-border);transition:background-color var(--opaca-transition)}.opaca-table-row:last-child{border-bottom:0}.opaca-table-row:hover{background-color:var(--opaca-surface)}.opaca-table-cell{padding:1rem;vertical-align:middle;color:var(--opaca-text);font-size:.875rem;border-bottom:1px solid var(--opaca-border);max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opaca-table-cell.opaca-cell-interactive{cursor:pointer;transition:all .2s ease;border-bottom:2px solid rgba(0,0,0,0)}.opaca-table-cell.opaca-cell-interactive:hover{background-color:hsla(0,0%,100%,.04);color:var(--opaca-primary);border-bottom-color:var(--opaca-primary)}.opaca-table-footer{background-color:hsla(0,0%,100%,.02);font-weight:500}.opaca-table-caption{margin-top:1rem;font-size:.875rem;color:var(--opaca-text-muted)}.opaca-item-button:hover{background-color:hsla(0,0%,100%,.05) !important;border-color:var(--opaca-primary) !important}.opaca-item-button.active{background-color:rgba(var(--opaca-primary-rgb), 0.1) !important;border-color:var(--opaca-primary) !important;color:var(--opaca-primary)}.opaca-ui-btn{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;border-radius:var(--opaca-radius);font-size:.875rem;font-weight:500;transition:all var(--opaca-transition);cursor:pointer;border:1px solid rgba(0,0,0,0);outline:none}.opaca-ui-btn:focus-visible{box-shadow:0 0 0 2px var(--opaca-bg),0 0 0 4px var(--opaca-primary)}.opaca-ui-btn:disabled{pointer-events:none;opacity:.5}.opaca-ui-btn-size-default{height:2.25rem;padding:.5rem 1rem}.opaca-ui-btn-size-sm{height:2rem;padding:0 .75rem;font-size:.75rem}.opaca-ui-btn-size-lg{height:2.5rem;padding:0 2rem}.opaca-ui-btn-size-icon{height:2.25rem;width:2.25rem;padding:0}.opaca-ui-btn-default{background-color:var(--opaca-primary);color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.opaca-ui-btn-default:hover{background-color:var(--opaca-primary-hover)}.opaca-ui-btn-destructive{background-color:var(--opaca-error);color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.opaca-ui-btn-destructive:hover{background-color:#ef4444}.opaca-ui-btn-outline{border-color:var(--opaca-border);background-color:rgba(0,0,0,0);color:var(--opaca-text);box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.opaca-ui-btn-outline:hover{background-color:hsla(0,0%,100%,.05)}.opaca-ui-btn-secondary{background-color:var(--opaca-surface);color:var(--opaca-text);border-color:var(--opaca-border);box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.opaca-ui-btn-secondary:hover{background-color:hsla(0,0%,100%,.05)}.opaca-ui-btn-ghost{background-color:rgba(0,0,0,0);color:var(--opaca-text)}.opaca-ui-btn-ghost:hover{background-color:hsla(0,0%,100%,.05)}.opaca-ui-btn-link{background-color:rgba(0,0,0,0);color:var(--opaca-primary);text-decoration:underline;text-underline-offset:4px}.opaca-ui-btn-link:hover{text-decoration:none}.opaca-btn{display:inline-flex;align-items:center;justify-content:center;gap:.375rem;padding:.5rem 1rem;border-radius:var(--opaca-radius);font-weight:500;font-size:.8125rem;cursor:pointer;transition:all var(--opaca-transition);border:none;font-family:inherit;line-height:1.4}.opaca-btn-primary{background-color:var(--opaca-primary);color:#fff}.opaca-btn-primary:hover{background-color:var(--opaca-primary-hover)}.opaca-btn-primary:disabled{opacity:.6;cursor:not-allowed}.opaca-btn-outline{background-color:rgba(0,0,0,0);border:1px solid var(--opaca-border);color:var(--opaca-text-muted)}.opaca-btn-outline:hover{border-color:var(--opaca-border-hover);color:var(--opaca-text);background-color:hsla(0,0%,100%,.02)}.opaca-item-button:hover{background-color:var(--opaca-brand-subtle) !important;border-color:var(--opaca-primary) !important;transform:translateY(-1px)}.opaca-item-button:active{transform:translateY(0)}.opaca-item-button.active{background-color:var(--opaca-brand-subtle) !important;border-color:var(--opaca-primary) !important;box-shadow:0 0 0 1px var(--opaca-primary)}.opaca-ui-input{display:flex;height:2.25rem;width:100%;border-radius:var(--opaca-radius);border:1px solid var(--opaca-border);background-color:var(--opaca-surface);padding:.25rem .75rem;font-size:.875rem;color:var(--opaca-text);box-shadow:0 1px 2px 0 rgba(0,0,0,.05);transition:border-color var(--opaca-transition),box-shadow var(--opaca-transition);outline:none}.opaca-ui-input::placeholder{color:var(--opaca-text-dim)}.opaca-ui-input:focus-visible{border-color:var(--opaca-primary);box-shadow:0 0 0 1px var(--opaca-primary)}.opaca-ui-input:disabled{cursor:not-allowed;opacity:.5}.opaca-ui-input[type=file]{border:0;background-color:rgba(0,0,0,0);font-size:.875rem;font-weight:500}.opaca-label{display:block;font-size:.75rem;font-weight:500;margin-bottom:.375rem;color:var(--opaca-text-muted);text-transform:uppercase;letter-spacing:.03em}.opaca-input{width:100%;padding:.5rem .75rem;border-radius:var(--opaca-radius);background-color:var(--opaca-surface);border:1px solid var(--opaca-border);color:var(--opaca-text);font-family:inherit;font-size:.8125rem;transition:border-color var(--opaca-transition),box-shadow var(--opaca-transition);outline:none}.opaca-input::placeholder{color:var(--opaca-text-dim)}.opaca-input:focus{border-color:var(--opaca-primary);box-shadow:0 0 0 1px var(--opaca-primary-glow)}.opaca-input.error{border-color:var(--opaca-error)}.opaca-input.error:focus{box-shadow:0 0 0 1px var(--opaca-error-bg)}.opaca-field-error{display:block;font-size:.75rem;color:var(--opaca-error);margin-top:.25rem}.opaca-badge{display:inline-flex;align-items:center;padding:.125rem .5rem;font-size:.6875rem;font-weight:500;border-radius:var(--opaca-radius);background-color:var(--opaca-primary-glow);color:var(--opaca-accent);letter-spacing:.02em}.opaca-toast-container{position:fixed;top:1.5rem;right:1.5rem;z-index:9999;display:flex;flex-direction:column;gap:.75rem;pointer-events:none}.opaca-toast{pointer-events:auto;min-width:280px;max-width:400px;background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);padding:.875rem 1rem;border-radius:var(--opaca-radius-lg);display:flex;align-items:center;gap:.75rem;box-shadow:0 10px 25px -5px rgba(0,0,0,.4);animation:opaca-toast-in 300ms cubic-bezier(0.16, 1, 0.3, 1);transition:all var(--opaca-transition)}.opaca-toast.exit{opacity:0;transform:translateX(20px)}.opaca-toast-success{border-left:3px solid var(--opaca-success)}.opaca-toast-error{border-left:3px solid var(--opaca-error)}.opaca-toast-info{border-left:3px solid var(--opaca-accent)}.opaca-toast-message{font-size:.8125rem;font-weight:500;color:var(--opaca-text);line-height:1.4}.opaca-switch{position:relative;display:inline-flex;align-items:center;cursor:pointer;user-select:none;gap:.75rem}.opaca-switch input{position:absolute;opacity:0;width:0;height:0}.opaca-switch input:checked+.opaca-switch-track{background-color:var(--opaca-accent);border-color:var(--opaca-accent)}.opaca-switch input:checked+.opaca-switch-track .opaca-switch-thumb{left:18px}.opaca-switch input:disabled+.opaca-switch-track{background:var(--opaca-border);cursor:not-allowed;opacity:.6}.opaca-switch-track{width:36px;height:20px;background-color:var(--opaca-surface);border:1px solid var(--opaca-border);border-radius:20px;position:relative;transition:all var(--opaca-transition)}.opaca-switch-thumb{width:14px;height:14px;background-color:#fff;border-radius:50%;position:absolute;top:2px;left:2px;transition:all var(--opaca-transition);box-shadow:0 2px 4px rgba(0,0,0,.2)}.opaca-switch-label{font-size:.8125rem;font-weight:500}.opaca-pagination{display:flex;align-items:center;justify-content:space-between;padding:1rem;border-top:1px solid var(--opaca-border);background-color:var(--opaca-sidebar-bg)}.opaca-pagination-info{font-size:.75rem;color:var(--opaca-text-dim)}.opaca-pagination-actions{display:flex;gap:.5rem}.opaca-empty{padding:5rem 2rem;text-align:center;display:flex;flex-direction:column;align-items:center;gap:1rem}.opaca-empty-icon{width:48px;height:48px;border-radius:50%;background-color:var(--opaca-surface);display:flex;align-items:center;justify-content:center;color:var(--opaca-text-dim);margin-bottom:.5rem}.opaca-loading-screen{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;width:100%;background-color:var(--opaca-bg);color:var(--opaca-text);gap:2rem}.opaca-loading-logo-container{position:relative;display:flex;align-items:center;justify-content:center}.opaca-loading-logo-pulse{position:absolute;width:64px;height:64px;background-color:var(--opaca-primary);border-radius:var(--opaca-radius);opacity:.2;animation:opaca-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.opaca-loading-status{display:flex;align-items:center;gap:.75rem;font-size:.875rem;color:var(--opaca-text-muted);animation:opaca-fade-in 1s ease-out}.opaca-login-container{display:flex;align-items:center;justify-content:center;min-height:100vh;width:100%;background-color:var(--opaca-bg);font-family:var(--opaca-font);color:var(--opaca-text);-webkit-font-smoothing:antialiased}.opaca-login-card{width:100%;max-width:380px;background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);padding:2rem}.opaca-login-header{text-align:center;margin-bottom:1.5rem}.opaca-login-logo{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background-color:var(--opaca-primary);border-radius:var(--opaca-radius);margin:0 auto 1rem}.opaca-logo-circle{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background-color:var(--opaca-primary);border-radius:var(--opaca-radius);color:#fff}.opaca-login-error{padding:.625rem .75rem;background-color:var(--opaca-error-bg);border:1px solid rgba(248,113,113,.2);color:var(--opaca-error);border-radius:var(--opaca-radius);margin-bottom:1.25rem;display:flex;align-items:center;gap:.5rem;font-size:.8125rem}.opaca-lexical-wrapper{display:flex;flex-direction:column;gap:.75rem}.opaca-lexical-modes{display:inline-flex;gap:2px;background-color:var(--opaca-surface);padding:3px;border-radius:var(--opaca-radius-lg);border:1px solid var(--opaca-border);align-self:flex-start;margin-bottom:.25rem}.opaca-lexical-modes .opaca-btn{padding:.4rem .8rem;font-size:.75rem;border-radius:var(--opaca-radius);border:none;background-color:rgba(0,0,0,0);color:var(--opaca-text-muted);font-weight:500;min-width:80px;transition:all var(--opaca-transition)}.opaca-lexical-modes .opaca-btn:hover{color:var(--opaca-text);background-color:hsla(0,0%,100%,.05)}.opaca-lexical-modes .opaca-btn.opaca-btn-primary{background-color:var(--opaca-primary);color:#fff;box-shadow:0 2px 4px rgba(0,0,0,.2)}.opaca-lexical-container{border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);background-color:var(--opaca-surface);overflow:hidden;transition:all var(--opaca-transition);display:flex;flex-direction:column}.opaca-lexical-container:focus-within{border-color:var(--opaca-accent);box-shadow:0 0 0 2px var(--opaca-primary-glow)}.opaca-lexical-container.mode-notion .opaca-lexical-content{padding:1.5rem 1rem}.opaca-lexical-container.is-readonly{background-color:hsla(0,0%,100%,.02);cursor:not-allowed}.opaca-lexical-container.is-readonly .opaca-lexical-content{padding:0rem}.opaca-lexical-toolbar{display:flex;flex-wrap:wrap;gap:.125rem;padding:.375rem;border-bottom:1px solid var(--opaca-border);background-color:var(--opaca-card-bg);align-items:center}.opaca-lexical-bubble-menu{background:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);padding:4px;display:flex;gap:4px;box-shadow:0 10px 15px -3px rgba(0,0,0,.4);backdrop-filter:blur(8px)}.opaca-lexical-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--opaca-radius);background:rgba(0,0,0,0);border:none;color:var(--opaca-text-muted);cursor:pointer;transition:all var(--opaca-transition)}.opaca-lexical-btn:hover{background-color:hsla(0,0%,100%,.08);color:var(--opaca-text)}.opaca-lexical-btn.is-active{background-color:var(--opaca-primary-glow);color:var(--opaca-accent)}.opaca-lexical-divider{width:1px;height:18px;background-color:var(--opaca-border);margin:0 .25rem}.opaca-lexical-placeholder{color:var(--opaca-text-dim);position:absolute;top:1rem;left:1rem;pointer-events:none;font-style:normal;font-size:.875rem;opacity:.5}.opaca-lexical-editor-inner{position:relative;flex:1}.opaca-lexical-content{min-height:160px;padding:1rem;outline:none;line-height:1.6;color:var(--opaca-text);font-size:.9375rem}.opaca-lexical-content>*:first-child{margin-top:0}.opaca-lexical-content>*:last-child{margin-bottom:0}.opaca-lexical-content p{margin:.75em 0}.opaca-lexical-content .editor-heading-h1{font-size:2.25rem;font-weight:700;margin-top:1.5rem;margin-bottom:.75rem;color:var(--opaca-text)}.opaca-lexical-content .editor-heading-h2{font-size:1.875rem;font-weight:600;margin-top:1.25rem;margin-bottom:.5rem;color:var(--opaca-text)}.opaca-lexical-content .editor-text-bold{font-weight:700}.opaca-lexical-content .editor-text-italic{font-style:italic}.opaca-lexical-content .editor-text-underline{text-decoration:underline}.opaca-lexical-content .editor-text-strikethrough{text-decoration:line-through}.opaca-lexical-content .editor-text-code{background-color:hsla(0,0%,100%,.05);padding:2px 4px;border-radius:4px;font-family:monospace;font-size:.9em}.opaca-lexical-content ul{list-style-type:disc;padding-left:1.5rem;margin:.5rem 0}.opaca-lexical-content ol{list-style-type:decimal;padding-left:1.5rem;margin:.5rem 0}.opaca-lexical-content .editor-listitem{margin:.25rem 0}.opaca-lexical-content ul[data-type=taskList]{list-style:none;padding:0}.opaca-lexical-content ul[data-type=taskList] p{margin:0}.opaca-lexical-content ul[data-type=taskList] li{display:flex;margin-bottom:.25rem}.opaca-lexical-content ul[data-type=taskList] li>label{flex:0 0 auto;margin-right:.5rem;user-select:none}.opaca-lexical-content ul[data-type=taskList] li>div{flex:1 1 auto}.opaca-lexical-content ul[data-type=taskList] li input[type=checkbox]{cursor:pointer;width:1rem;height:1rem;margin-top:.2rem;accent-color:var(--opaca-primary)}.opaca-lexical-content ul[data-type=taskList] li[data-checked=true]>div>p{text-decoration:line-through;color:var(--opaca-text-muted)}.opaca-lexical-content blockquote{border-left:3px solid var(--opaca-border);padding-left:1rem;margin:1em 0;color:var(--opaca-text-dim);font-style:italic}.opaca-lexical-content .editor-image-wrapper{position:relative;display:inline-block;cursor:default;user-select:none;line-height:0}.opaca-lexical-content .editor-image-wrapper.is-selected{outline:3px solid var(--opaca-primary);outline-offset:2px;border-radius:4px;box-shadow:0 0 0 4px var(--opaca-primary-glow)}.opaca-lexical-content .editor-image-wrapper .editor-image-img{max-width:100%;height:auto;border-radius:var(--opaca-radius)}.opaca-lexical-content .editor-image-wrapper .editor-image-resizer{position:absolute;right:-6px;bottom:-6px;width:14px;height:14px;background-color:var(--opaca-primary);border:2px solid #fff;cursor:nwse-resize;z-index:20;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.3)}.opaca-lexical-content .editor-image-wrapper .editor-image-resizer:hover{background-color:var(--opaca-accent);transform:scale(1.2)}.opaca-lexical-content .editor-image-wrapper .editor-image-delete{position:absolute;top:-8px;right:-8px;width:24px;height:24px;background-color:var(--opaca-error);color:#fff;border:1px solid #fff;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:10;box-shadow:0 2px 4px rgba(0,0,0,.3);padding:0;transition:transform .2s}.opaca-lexical-content .editor-image-wrapper .editor-image-delete:hover{transform:scale(1.1);background-color:#ff4d4d}.opaca-lexical-content a{color:var(--opaca-primary);text-decoration:underline;text-underline-offset:2px}.opaca-lexical-content pre{background-color:var(--opaca-bg);color:var(--opaca-text);padding:1rem;border-radius:var(--opaca-radius);overflow-x:auto;border:1px solid var(--opaca-border);margin:1em 0}.opaca-lexical-content pre code{color:inherit;padding:0;background:none;font-size:.85rem}.opaca-lexical-content [contenteditable=true] p.is-editor-empty:first-child::before,.opaca-lexical-content [contenteditable=true] h1.is-empty::before,.opaca-lexical-content [contenteditable=true] h2.is-empty::before,.opaca-lexical-content [contenteditable=true] h3.is-empty::before,.opaca-lexical-content [contenteditable=true] [data-placeholder]::before{content:attr(data-placeholder);float:left;color:var(--opaca-text-muted);pointer-events:none;height:0;font-style:italic;opacity:.6}.opaca-lexical-bubble-menu{display:flex;background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);padding:.25rem;gap:.125rem}.opaca-lexical-floating-menu{display:flex;align-items:center;margin-left:-32px}.opaca-lexical-btn-plus{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:var(--opaca-surface);border:1px solid var(--opaca-border);color:var(--opaca-text-muted);cursor:pointer;transition:all var(--opaca-transition);font-size:1rem;margin-right:.5rem}.opaca-lexical-btn-plus:hover{background-color:var(--opaca-primary-glow);color:var(--opaca-primary);border-color:var(--opaca-primary)}.opaca-lexical-floating-menu-items{display:none;background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);padding:.25rem;gap:.125rem;animation:opaca-fade-in 100ms ease-out}.opaca-lexical-floating-menu:hover .opaca-lexical-floating-menu-items{display:flex}.opaca-slash-menu{display:flex;flex-direction:column;background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);padding:.5rem;max-height:300px;overflow-y:auto;min-width:250px}.opaca-slash-menu-item{display:flex;align-items:center;gap:.75rem;padding:.3rem;border:none;background:rgba(0,0,0,0);color:var(--opaca-text);border-radius:var(--opaca-radius);cursor:pointer;text-align:left;transition:background-color var(--opaca-transition)}.opaca-slash-menu-item:hover,.opaca-slash-menu-item.is-selected{background-color:var(--opaca-surface)}.opaca-slash-menu-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;background-color:var(--opaca-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius);color:var(--opaca-text-muted)}.opaca-slash-menu-text{display:flex;flex-direction:column}.opaca-slash-menu-title{font-size:.8125rem;font-weight:500;color:var(--opaca-text)}.opaca-slash-menu-desc{font-size:.7rem;color:var(--opaca-text-muted)}.opaca-slash-menu-empty{padding:1rem;color:var(--opaca-text-muted);font-size:.8125rem;text-align:center}.opaca-sidebar-accordion{margin-bottom:.25rem;border:none !important;background:rgba(0,0,0,0) !important}.opaca-sidebar-accordion .opaca-sidebar-accordion-trigger{background:rgba(0,0,0,0);padding:1rem .75rem .5rem .75rem;font-size:.625rem;color:var(--opaca-text-dim);text-transform:uppercase;letter-spacing:.06em;font-weight:500;width:100%;display:flex;align-items:center;justify-content:space-between;cursor:pointer;transition:color var(--opaca-transition);border:none;outline:none}.opaca-sidebar-accordion .opaca-sidebar-accordion-trigger:hover{color:var(--opaca-text)}.opaca-sidebar-accordion .opaca-sidebar-accordion-trigger .opaca-sidebar-accordion-icon{width:12px;height:12px;stroke:var(--opaca-text-dim);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform .2s cubic-bezier(0.87, 0, 0.13, 1)}.opaca-sidebar-accordion .opaca-sidebar-accordion-trigger[aria-expanded=true] .opaca-sidebar-accordion-icon{transform:rotate(180deg)}.opaca-sidebar-accordion .opaca-sidebar-accordion-content{display:grid;grid-template-rows:0fr;transition:grid-template-rows .2s cubic-bezier(0.87, 0, 0.13, 1)}.opaca-sidebar-accordion .opaca-sidebar-accordion-content[data-state=open]{grid-template-rows:1fr}.opaca-sidebar-accordion .opaca-sidebar-accordion-content .opaca-sidebar-accordion-content-inner{overflow:hidden;display:flex;flex-direction:column}.asset-manager-overlay{position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;background-color:rgba(0,0,0,.5);padding:1rem}.asset-manager-container{background-color:var(--opaca-card-bg);border-radius:var(--opaca-radius);box-shadow:0 25px 50px -12px rgba(0,0,0,.5);width:100%;border:1px solid var(--opaca-border);max-width:56rem;height:600px;display:flex;flex-direction:column;color:var(--opaca-text);overflow:hidden}.asset-manager-header{padding:1.25rem 1.75rem;border-bottom:1px solid var(--opaca-border);display:flex;justify-content:space-between;align-items:center;background-color:var(--opaca-card-bg)}.asset-manager-header h2{font-size:1.125rem;font-weight:600;margin:0}.asset-manager-header .asset-manager-breadcrumbs{display:flex;align-items:center;gap:.5rem;margin-top:.25rem;font-size:.8125rem;color:var(--opaca-text-dim)}.asset-manager-header .asset-manager-breadcrumbs button{background:none;border:none;padding:0;color:inherit;cursor:pointer}.asset-manager-header .asset-manager-breadcrumbs button:hover{color:var(--opaca-accent);text-decoration:underline}.asset-manager-header .asset-manager-breadcrumbs .breadcrumb-separator{opacity:.5}.asset-manager-header .header-actions{display:flex;align-items:center;gap:1rem}.asset-manager-header .header-actions .bucket-selector{min-width:140px;height:36px}.asset-manager-header .close-button{color:var(--opaca-text-dim);background:rgba(0,0,0,0);border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all var(--opaca-transition);padding:.25rem;border-radius:6px}.asset-manager-header .close-button:hover{background-color:var(--opaca-surface);color:var(--opaca-text)}.asset-manager-upload-zone{padding:2rem;border-bottom:2px dashed var(--opaca-border);background-color:rgba(0,0,0,.2);cursor:pointer;text-align:center;position:relative;transition:all .2s;display:flex;flex-direction:column;align-items:center;justify-content:center}.asset-manager-upload-zone.uploading{pointer-events:none;opacity:.7}.asset-manager-upload-zone .upload-icon{margin-bottom:.75rem;color:var(--opaca-text-dim)}.asset-manager-upload-zone .upload-prompt{color:var(--opaca-text-dim);margin:0;font-size:.875rem}.asset-manager-upload-zone .upload-prompt span{color:var(--opaca-accent);font-weight:600}.asset-manager-upload-zone .uploading-status{display:flex;flex-direction:column;align-items:center;gap:.75rem}.asset-manager-upload-zone .uploading-status .status-text{color:var(--opaca-accent);font-weight:600;font-size:.875rem}.asset-manager-grid-container{flex:1;overflow-y:auto;padding:1.5rem;background-color:rgba(0,0,0,.1)}.asset-manager-grid-container .loading-assets,.asset-manager-grid-container .no-assets{display:flex;justify-content:center;align-items:center;height:100%;color:#9ca3af}.asset-manager-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(140px, 1fr));gap:1.25rem}.asset-manager-card{position:relative;background-color:var(--opaca-surface);border-radius:12px;border:1px solid var(--opaca-border);overflow:hidden;cursor:pointer;transition:all var(--opaca-transition);display:flex;flex-direction:column}.asset-manager-card:hover{border-color:var(--opaca-accent);transform:translateY(-2px);box-shadow:var(--opaca-shadow-lg)}.asset-manager-card:hover .selection-overlay{opacity:1}.asset-manager-card .asset-thumb{aspect-ratio:16/10;display:flex;align-items:center;justify-content:center;background-color:var(--opaca-bg-alt);overflow:hidden;position:relative}.asset-manager-card .asset-thumb img{width:100%;height:100%;object-fit:cover}.asset-manager-card .asset-thumb .folder-icon{color:#eab308}.asset-manager-card .asset-info{padding:.75rem;display:flex;flex-direction:column;gap:.25rem;background-color:var(--opaca-card-bg);flex:1}.asset-manager-card .asset-info .filename{font-size:.8125rem;font-weight:500;color:var(--opaca-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.asset-manager-card .asset-info .file-meta{font-size:.6875rem;color:var(--opaca-text-dim)}.asset-manager-card .selection-overlay{position:absolute;inset:0;background-color:rgba(var(--opaca-accent-rgb), 0.1);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity var(--opaca-transition);pointer-events:none}.asset-manager-card .selection-overlay .select-badge{background-color:var(--opaca-accent);color:#fff;font-size:.75rem;font-weight:600;padding:.375rem .75rem;border-radius:9999px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1)}.asset-manager-card.folder-card .asset-thumb{background-color:rgba(234,179,8,.05)}.file-field-container{margin-bottom:2rem;background-color:rgba(0,0,0,0);padding:0;border:none;box-shadow:none}.file-field-label-row{display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem}.file-field-label-row label{display:block;font-size:.875rem;font-weight:500;color:var(--opaca-text);text-transform:capitalize}.file-field-label-row .upload-error{font-size:.75rem;color:#ef4444;font-weight:500}.file-field-optimistic-card{display:flex;flex-direction:column;gap:.5rem;padding:1rem;border:1px solid var(--opaca-primary);border-radius:var(--opaca-radius);background-color:rgba(var(--opaca-primary-rgb), 0.1)}.file-field-optimistic-card .optimistic-info{display:flex;align-items:center;gap:1rem;opacity:.7}.file-field-optimistic-card .optimistic-info img{width:4rem;height:4rem;object-fit:cover;border-radius:.25rem;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.file-field-optimistic-card .optimistic-info .file-icon-placeholder{width:4rem;height:4rem;background-color:#e5e7eb;border-radius:.25rem;display:flex;align-items:center;justify-content:center}.file-field-optimistic-card .optimistic-info .file-icon-placeholder span{font-size:.75rem;font-family:monospace}.file-field-optimistic-card .optimistic-info .optimistic-details{display:flex;flex-direction:column;flex:1;min-width:0}.file-field-optimistic-card .optimistic-info .optimistic-details .filename{font-size:.875rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-field-optimistic-card .optimistic-info .optimistic-details .progress-text{font-size:.75rem;color:#2563eb;margin-bottom:.25rem;font-weight:600}.file-field-optimistic-card .optimistic-info .optimistic-details .progress-bar-bg{width:100%;background-color:#bfdbfe;border-radius:9999px;height:.375rem}.file-field-optimistic-card .optimistic-info .optimistic-details .progress-bar-bg .progress-bar-fill{background-color:#2563eb;height:100%;border-radius:9999px;transition:width .3s}.file-field-asset-card{display:flex;flex-direction:column;gap:1rem}.file-field-asset-info{display:flex;align-items:center;padding:1rem;border:1px solid var(--opaca-border);border-radius:var(--opaca-radius);background-color:var(--opaca-card-bg);justify-content:space-between}.file-field-asset-info .asset-preview{display:flex;align-items:center;gap:1rem}.file-field-asset-info .asset-preview img{width:4rem;height:4rem;object-fit:cover;border-radius:.25rem;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);border:1px solid #e5e7eb}.file-field-asset-info .asset-preview .file-icon-placeholder{width:4rem;height:4rem;background-color:var(--opaca-border);border-radius:.25rem;display:flex;align-items:center;justify-content:center;border:1px solid var(--opaca-border)}.file-field-asset-info .asset-preview .file-icon-placeholder span{font-size:.75rem;font-family:monospace;color:var(--opaca-subtitle)}.file-field-asset-info .asset-preview .asset-details{display:flex;flex-direction:column}.file-field-asset-info .asset-preview .asset-details .filename{font-size:.875rem;font-weight:500;color:var(--opaca-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px}.file-field-asset-info .asset-preview .asset-details .filesize{font-size:.75rem;color:var(--opaca-subtitle);font-family:monospace;margin-top:.125rem}.file-field-asset-info .asset-actions{display:flex;gap:.75rem}.file-field-asset-info .asset-actions button{font-size:.875rem;font-weight:500;background:rgba(0,0,0,0);border:none;cursor:pointer;transition:color .2s}.file-field-asset-info .asset-actions button.replace-button{color:var(--opaca-primary)}.file-field-asset-info .asset-actions button.replace-button:hover{opacity:.8}.file-field-asset-info .asset-actions button.remove-button{color:var(--opaca-error)}.file-field-asset-info .asset-actions button.remove-button:hover{opacity:.8}.file-field-metadata{padding-left:1rem;border-left:2px solid #bfdbfe;display:flex;flex-direction:column;gap:.75rem;padding-top:.5rem}.file-field-metadata h4{font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;color:#6b7280;font-weight:600;margin-bottom:.5rem}.file-field-metadata .metadata-field label{display:block;font-size:.75rem;font-weight:500;color:var(--opaca-subtitle);margin-bottom:.25rem;text-transform:capitalize}.file-field-metadata .metadata-field label .required{color:#ef4444}.file-field-metadata .metadata-field input,.file-field-metadata .metadata-field textarea{width:100%;font-size:.875rem;border:1px solid #d1d5db;border-radius:.375rem;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);padding:.5rem .75rem;outline:none;transition:all .2s}.file-field-metadata .metadata-field input:focus,.file-field-metadata .metadata-field textarea:focus{border-color:#3b82f6;outline:1px solid #3b82f6}.file-field-empty-button{width:100%;padding:3.5rem 0;border:2px dashed var(--opaca-border);border-radius:var(--opaca-radius);color:var(--opaca-text-dim);background-color:var(--opaca-surface);cursor:pointer;transition:all var(--opaca-transition);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem}.file-field-empty-button:hover{border-color:var(--opaca-primary);color:var(--opaca-accent);background-color:rgba(124,58,237,.05)}.file-field-empty-button svg{width:2rem;height:2rem;color:#9ca3af}.file-field-empty-button span{font-weight:500;font-size:.875rem}.opaca-ui-collapsible{display:flex;flex-direction:column;border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);background-color:rgba(0,0,0,0);overflow:hidden;margin-bottom:2rem;transition:border-color var(--opaca-transition)}.opaca-ui-collapsible-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;padding:1rem 1.25rem;font-size:.875rem;font-weight:500;text-align:left;background-color:var(--opaca-surface);color:var(--opaca-text);border:none;border-bottom:1px solid rgba(0,0,0,0);cursor:pointer;transition:all var(--opaca-transition)}.opaca-ui-collapsible-trigger:hover{background-color:var(--opaca-border)}.opaca-ui-collapsible-trigger[aria-expanded=true]{border-bottom-color:var(--opaca-border)}.opaca-ui-collapsible-trigger[aria-expanded=true] .opaca-ui-collapsible-icon{transform:rotate(180deg)}.opaca-ui-collapsible-icon{width:.875rem;height:.875rem;transition:transform var(--opaca-transition);fill:none;stroke:var(--opaca-text-muted);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.opaca-ui-collapsible-content{padding:1.25rem;background-color:rgba(0,0,0,0);color:var(--opaca-text)}.opaca-ui-collapsible-content[data-state=closed]{display:none}.opaca-ui-collapsible-content[data-state=open]{animation:collapsible-slide-down 200ms ease-out}@keyframes collapsible-slide-down{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.opaca-ui-dialog-portal{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center}.opaca-ui-dialog-overlay{position:fixed;inset:0;background-color:rgba(0,0,0,.8);backdrop-filter:blur(4px);transition:opacity .2s;border:none;padding:0;margin:0;cursor:default;width:100%;height:100%}.opaca-ui-dialog-wrapper{position:relative;z-index:10000;width:100%;height:100%;padding:1rem;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none}.opaca-ui-dialog-content{pointer-events:auto;position:relative;width:100%;max-height:calc(100vh - 2rem);overflow-y:auto;border-radius:var(--opaca-radius-lg);border:1px solid var(--opaca-border);background-color:var(--opaca-card-bg);padding:1.5rem;box-shadow:0 20px 25px -5px rgba(0,0,0,.3),0 8px 10px -6px rgba(0,0,0,.2);color:var(--opaca-text);animation:opacaDialogIn .2s cubic-bezier(0.16, 1, 0.3, 1)}.opaca-ui-dialog-content.opaca-dialog-max-w{max-width:425px}@keyframes opacaDialogIn{from{opacity:0;transform:scale(0.95)}to{opacity:1;transform:scale(1)}}.opaca-ui-dialog-header{display:flex;flex-direction:column;gap:.375rem;text-align:center}@media(min-width: 640px){.opaca-ui-dialog-header{text-align:left}}.opaca-ui-dialog-title{font-size:1.125rem;font-weight:600;line-height:1;letter-spacing:-0.025em;color:var(--opaca-text)}.opaca-ui-dialog-description{font-size:.875rem;color:var(--opaca-text-muted)}.opaca-ui-dialog-footer{display:flex;flex-direction:column-reverse;margin-top:1rem;gap:.5rem}@media(min-width: 640px){.opaca-ui-dialog-footer{flex-direction:row;justify-content:flex-end;gap:.5rem}}.opaca-ui-group{display:flex;flex-direction:column;gap:1.25rem;padding:1.5rem;border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);background-color:rgba(0,0,0,0);margin-bottom:2rem}.opaca-ui-group-header{font-size:.75rem;font-weight:500;margin-bottom:.5rem;color:var(--opaca-text-muted);text-transform:uppercase;letter-spacing:.03em}.media-registry-view{display:flex;flex-direction:column;height:100%}.media-registry-header{margin-bottom:2rem;flex-shrink:0}.media-registry-header .opaca-title{font-size:1.5rem;font-weight:600;letter-spacing:-0.025em}.media-registry-header .opaca-subtitle{font-size:.875rem;color:var(--opaca-text-muted);margin-top:.25rem}.media-registry-header-actions{display:flex;gap:.75rem}.media-registry-icon-mr{margin-right:.5rem}.media-registry-body{flex:1;display:flex;flex-direction:column;padding:0;overflow:visible;border:none;background:rgba(0,0,0,0)}.media-registry-toolbar{display:flex;justify-content:space-between;align-items:center;padding:1rem;background:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg, 0.5rem);margin-bottom:1.5rem;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.media-registry-filters{display:flex;gap:.75rem;align-items:center;width:100%;max-width:600px}.media-registry-search{position:relative;flex:1;min-width:320px}.media-registry-search .search-icon{position:absolute;left:.75rem;top:50%;transform:translateY(-50%);color:var(--opaca-text-dim)}.media-registry-search .opaca-ui-input{padding-left:2.5rem}.media-registry-select-trigger{width:140px;flex-shrink:0}.media-registry-view-toggles{display:flex;gap:.25rem;background:var(--opaca-surface);padding:.25rem;border-radius:var(--opaca-radius-lg, 0.5rem);border:1px solid var(--opaca-border)}.media-registry-toggle-btn{padding:.4rem;border-radius:var(--opaca-radius, 0.375rem);border:none;cursor:pointer;transition:all .2s;background:rgba(0,0,0,0);color:var(--opaca-text-dim);display:flex;align-items:center;justify-content:center}.media-registry-toggle-btn:hover{color:#fff}.media-registry-toggle-btn.active{background-color:var(--opaca-primary);color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.media-registry-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));gap:1.5rem;margin-top:.5rem}.media-registry-card{position:relative;background:var(--opaca-card-bg);border-radius:.75rem;border:1px solid var(--opaca-border);overflow:hidden;cursor:pointer;transition:all .2s;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.media-registry-card:hover{border-color:var(--opaca-border-hover)}.media-registry-card.active{border-color:var(--opaca-primary);box-shadow:0 0 0 1px var(--opaca-primary)}.media-registry-card-thumb{height:160px;background:var(--opaca-surface);display:flex;align-items:center;justify-content:center;border-bottom:1px solid var(--opaca-border);position:relative;overflow:hidden}.media-registry-card-thumb img{width:100%;height:100%;object-fit:cover;transition:transform .5s}.media-registry-card-thumb svg.folder-icon{color:#eab308;transition:transform .3s}.media-registry-card-thumb .media-registry-overlay{position:absolute;inset:0;background:rgba(0,0,0,.4);opacity:0;transition:opacity .2s;display:flex;align-items:center;justify-content:center}.media-registry-card:hover .media-registry-card-thumb img,.media-registry-card:hover .media-registry-card-thumb svg.folder-icon{transform:scale(1.05)}.media-registry-card:hover .media-registry-overlay{opacity:1}.media-registry-overlay-btn{border-radius:9999px;background-color:hsla(0,0%,100%,.1);backdrop-filter:blur(12px);color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;transition:all .2s}.media-registry-overlay-btn:hover{background-color:#fff;color:#000}.media-registry-card-body{padding:1rem}.media-registry-card-title{font-size:.875rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--opaca-text)}.media-registry-card-meta{font-size:.75rem;color:var(--opaca-text-dim);display:flex;justify-content:space-between;margin-top:.375rem}.media-registry-card-meta .meta-type{text-transform:uppercase;letter-spacing:.05em;font-size:.65rem;font-weight:600}.media-registry-pagination{display:flex;align-items:center;justify-content:space-between;padding:1rem;background:var(--opaca-surface);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg, 0.5rem);margin-top:2rem}.media-registry-pagination .info{font-size:.875rem;color:var(--opaca-text-muted)}.media-registry-pagination .actions{display:flex;gap:.5rem}.media-sheet-flex{flex:1;display:flex;flex-direction:column}.media-sheet-body{flex:1;overflow-y:auto;padding-right:.5rem;margin-right:-0.5rem}.media-sheet-preview{height:12rem;border-radius:var(--opaca-radius-lg, 0.5rem);background:var(--opaca-surface);border:1px solid var(--opaca-border);display:flex;align-items:center;justify-content:center;overflow:hidden;margin-bottom:1rem;position:relative}.media-sheet-preview img{width:100%;height:100%;object-fit:contain}.media-sheet-preview .overlay{position:absolute;inset:0;background:rgba(0,0,0,.4);opacity:0;transition:opacity .2s;display:flex;align-items:center;justify-content:center}.media-sheet-preview:hover .overlay{opacity:1}.media-sheet-form{display:flex;flex-direction:column;gap:1rem}.media-sheet-group{display:flex;flex-direction:column;gap:.5rem}.media-sheet-textarea{display:flex;min-height:80px;width:100%;border-radius:var(--opaca-radius, 0.375rem);border:1px solid var(--opaca-border);background-color:var(--opaca-surface);padding:.5rem .75rem;font-size:.875rem;color:var(--opaca-text);outline:none;font-family:inherit}.media-sheet-textarea::placeholder{color:var(--opaca-text-dim)}.media-sheet-textarea:focus-visible{border-color:var(--opaca-primary);box-shadow:0 0 0 1px var(--opaca-primary)}.media-sheet-meta-box{background:hsla(0,0%,100%,.02);padding:1rem;border-radius:var(--opaca-radius-lg, 0.5rem);border:1px solid var(--opaca-border);display:flex;flex-direction:column;gap:.75rem;margin-top:1.5rem}.media-sheet-meta-row{display:flex;justify-content:space-between;align-items:center;font-size:.875rem}.media-sheet-meta-row .label{color:var(--opaca-text-dim)}.media-sheet-meta-row .value{color:#fff;font-weight:500}.media-sheet-meta-row .value-mono{font-family:monospace;font-size:.75rem;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.media-sheet-actions{display:flex;gap:.75rem;padding-top:1.5rem;margin-top:1.5rem}.media-sheet-actions button{flex:1}.media-preview-container{flex:1;display:flex;flex-direction:column;padding:1.5rem;background:rgba(15,15,15,.7);backdrop-filter:blur(24px) saturate(180%);border-radius:.5rem;overflow:hidden;border:1px solid hsla(0,0%,100%,.1);box-shadow:0 25px 50px -12px rgba(0,0,0,.5);max-width:90vw;max-height:90vh;animation:opaca-zoom-in .3s cubic-bezier(0.16, 1, 0.3, 1)}@media(min-width: 640px){.media-preview-container{padding:1rem}}@keyframes opaca-zoom-in{from{opacity:0;transform:scale(0.95)}to{opacity:1;transform:scale(1)}}.media-preview-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1.5rem;flex-shrink:0;z-index:10}.media-preview-title-group{display:flex;align-items:center;gap:.75rem}.media-preview-title-group h2{font-size:1.25rem;font-weight:600;color:#fff;margin:0}.media-preview-title-group .badge{padding:.125rem .5rem;border-radius:.25rem;font-size:.75rem;font-family:monospace;background:hsla(0,0%,100%,.1);color:#fff;border:1px solid hsla(0,0%,100%,.2)}.media-preview-close-btn{color:#fff;background:rgba(0,0,0,0);border-radius:9999px;height:2.5rem;width:2.5rem;display:flex;align-items:center;justify-content:center;border:none;cursor:pointer;transition:background .2s}.media-preview-close-btn:hover{background:hsla(0,0%,100%,.1)}.media-preview-body{flex:1;display:flex;align-items:center;justify-content:center;min-height:0;position:relative}.media-preview-body img{max-width:100%;max-height:100%;object-fit:contain;filter:drop-shadow(0 25px 25px rgba(0, 0, 0, 0.5))}.media-preview-no-rich{text-align:center;padding:2rem;background:hsla(0,0%,100%,.05);border-radius:1rem;border:1px solid hsla(0,0%,100%,.1);backdrop-filter:blur(4px)}.media-preview-no-rich .icon-wrapper{display:flex;justify-content:center;margin-bottom:1.5rem}.media-preview-no-rich p{color:var(--opaca-text-dim);margin-bottom:1.5rem}.media-preview-footer{margin-top:2rem;display:flex;justify-content:center;gap:1rem;flex-shrink:0}.media-preview-stat{background:hsla(0,0%,100%,.03);backdrop-filter:blur(8px);padding:.75rem 1.5rem;border-radius:1rem;border:1px solid hsla(0,0%,100%,.05);text-align:center;transition:all .2s}.media-preview-stat:hover{background:hsla(0,0%,100%,.06);transform:translateY(-2px)}.media-preview-stat .label{font-size:.75rem;color:var(--opaca-text-dim);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.25rem}.media-preview-stat .value{font-weight:600;color:#fff}.media-dialog-create-body{padding:1rem 0}.media-bucket-settings{display:flex;flex-direction:column;gap:1.5rem;padding:1rem 0}.media-bucket-settings .bucket-setting-row{display:flex;flex-direction:column;gap:.75rem}.media-bucket-settings .bucket-setting-row .bucket-name{font-size:.75rem;font-weight:600;color:var(--opaca-text-dim);text-transform:uppercase;letter-spacing:.05em}.media-bucket-settings .bucket-setting-row .color-presets{display:flex;flex-wrap:wrap;gap:.5rem}.media-bucket-settings .bucket-setting-row .color-presets .color-bubble{width:24px;height:24px;border-radius:50%;border:2px solid rgba(0,0,0,0);cursor:pointer;transition:all var(--opaca-transition);padding:0;display:flex;align-items:center;justify-content:center}.media-bucket-settings .bucket-setting-row .color-presets .color-bubble:hover{transform:scale(1.1)}.media-bucket-settings .bucket-setting-row .color-presets .color-bubble.active{border-color:#fff;box-shadow:0 0 0 2px var(--opaca-primary)}.media-registry-dialog-sm{max-width:400px}.opaca-ui-radio-group{display:grid;gap:.75rem}.opaca-ui-radio-item{display:flex;align-items:center;gap:.75rem}.opaca-ui-radio-item input[type=radio]{appearance:none;margin:0;width:1.125rem;height:1.125rem;border:1px solid var(--opaca-border);border-radius:50%;outline:none;cursor:pointer;position:relative;background-color:var(--opaca-surface);transition:all var(--opaca-transition)}.opaca-ui-radio-item input[type=radio]:hover:not(:disabled){border-color:var(--opaca-accent)}.opaca-ui-radio-item input[type=radio]:checked{border-color:var(--opaca-primary);background-color:var(--opaca-primary)}.opaca-ui-radio-item input[type=radio]:checked::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:.375rem;height:.375rem;border-radius:50%;background-color:#fff}.opaca-ui-radio-item input[type=radio]:focus-visible{box-shadow:0 0 0 2px var(--opaca-primary-glow)}.opaca-ui-radio-item input[type=radio]:disabled{cursor:not-allowed;opacity:.5}.opaca-ui-radio-item label{cursor:pointer;font-size:.8125rem;font-weight:400;color:var(--opaca-text)}.opaca-ui-radio-item label.disabled{cursor:not-allowed;opacity:.5}.opaca-ui-select{position:relative;width:100%}.opaca-ui-select-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;min-width:160px;padding:.5rem .75rem;font-size:.8125rem;background-color:var(--opaca-surface);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius);color:var(--opaca-text);cursor:pointer;transition:all var(--opaca-transition);text-align:left;outline:none}.opaca-ui-select-trigger:hover{border-color:var(--opaca-border-hover);background-color:hsla(0,0%,100%,.02)}.opaca-ui-select-trigger:focus{border-color:var(--opaca-primary);box-shadow:0 0 0 1px var(--opaca-primary-glow)}.opaca-ui-select-trigger:disabled{opacity:.5;cursor:not-allowed}.opaca-ui-select-trigger .trigger-placeholder{color:var(--opaca-text-dim)}.opaca-ui-select-trigger .trigger-icon{color:var(--opaca-text-dim);transition:transform var(--opaca-transition)}.opaca-ui-select-trigger[data-state=open] .trigger-icon{transform:rotate(180deg)}.opaca-ui-select-portal{position:fixed;z-index:1000;pointer-events:none}.opaca-ui-select-content{pointer-events:auto;min-width:8rem;overflow:hidden;background-color:var(--opaca-card-bg);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);box-shadow:0 10px 25px -5px rgba(0,0,0,.4);padding:.25rem;animation:opaca-select-in 150ms cubic-bezier(0.16, 1, 0.3, 1);transform-origin:top}@keyframes opaca-select-in{from{opacity:0;transform:scale(0.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}.opaca-ui-select-item{display:flex;align-items:center;width:100%;padding:.375rem .5rem;font-size:.8125rem;color:var(--opaca-text-muted);border-radius:var(--opaca-radius);cursor:pointer;transition:all var(--opaca-transition);border:none;background:rgba(0,0,0,0);text-align:left}.opaca-ui-select-item:hover{background-color:hsla(0,0%,100%,.05);color:var(--opaca-text)}.opaca-ui-select-item[data-selected=true]{background-color:var(--opaca-primary-glow);color:var(--opaca-accent);font-weight:500}.opaca-ui-select-label{padding:.375rem .5rem;font-size:.75rem;font-weight:600;color:var(--opaca-text-dim);text-transform:uppercase;letter-spacing:.05em}.opaca-ui-select-separator{height:1px;background-color:var(--opaca-border);margin:.25rem -0.25rem}.opaca-ui-sheet-portal{position:fixed;inset:0;z-index:9999;display:flex;justify-content:flex-end}.opaca-ui-sheet-overlay{position:fixed;inset:0;background-color:rgba(0,0,0,.8);backdrop-filter:blur(4px);transition:opacity .2s;border:none;padding:0;margin:0;cursor:default;width:100%;height:100%}.opaca-ui-sheet-wrapper{position:relative;z-index:10000;height:100%;pointer-events:none;display:flex;justify-content:flex-end;width:100%}.opaca-ui-sheet-content{pointer-events:auto;position:relative;z-index:50;display:flex;height:100%;width:100%;flex-direction:column;border-left:1px solid var(--opaca-border);background-color:var(--opaca-card-bg);padding:1.5rem;box-shadow:-10px 0 25px -5px rgba(0,0,0,.2);overflow-y:auto;animation:opacaSlideInRight .3s cubic-bezier(0.16, 1, 0.3, 1);color:var(--opaca-text)}@media(min-width: 640px){.opaca-ui-sheet-content{width:400px;max-width:100vw}}@media(min-width: 1024px){.opaca-ui-sheet-content{width:500px}}.opaca-ui-sheet-close{position:absolute;right:1rem;top:1rem;border-radius:50%;opacity:.7;transition:all .2s ease;background:rgba(0,0,0,0);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:.5rem}.opaca-ui-sheet-close:hover{opacity:1;background-color:hsla(0,0%,100%,.05);color:var(--opaca-text)}.opaca-ui-sheet-close:focus-visible{outline:none;box-shadow:0 0 0 2px var(--opaca-primary)}.opaca-ui-sheet-close-icon{height:1rem;width:1rem;color:var(--opaca-text-muted)}.opaca-ui-sheet-header{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1.5rem;text-align:center}@media(min-width: 640px){.opaca-ui-sheet-header{text-align:left}}.opaca-ui-sheet-title{font-size:1.125rem;font-weight:600;color:var(--opaca-text);margin:0}.opaca-ui-sheet-description{font-size:.875rem;color:var(--opaca-text-muted);margin:0}.opaca-ui-sheet-footer{display:flex;flex-direction:column-reverse;margin-top:auto;padding-top:1.5rem;border-top:1px solid var(--opaca-border);gap:.75rem}@media(min-width: 640px){.opaca-ui-sheet-footer{flex-direction:row;justify-content:flex-end;gap:.5rem}}.opaca-ui-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0}@keyframes opacaSlideInRight{from{transform:translateX(100%)}to{transform:translateX(0)}}.opaca-ui-tabs{display:flex;flex-direction:column;width:100%}.opaca-ui-tabs-list{display:flex;background-color:var(--opaca-surface);padding:.25rem;border-radius:var(--opaca-radius-lg);overflow-x:auto;gap:.25rem;align-items:center;justify-content:flex-start;border:1px solid var(--opaca-border)}.opaca-ui-tabs-trigger{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;border-radius:var(--opaca-radius);padding:.5rem 1rem;font-size:.8125rem;font-weight:500;color:var(--opaca-text-muted);background-color:rgba(0,0,0,0);border:none;cursor:pointer;transition:all var(--opaca-transition)}.opaca-ui-tabs-trigger:hover{color:var(--opaca-text);background-color:hsla(0,0%,100%,.05)}.opaca-ui-tabs-trigger[data-state=active]{background-color:var(--opaca-primary);color:#fff;box-shadow:0 4px 12px var(--opaca-primary-glow)}.opaca-ui-tabs-trigger:disabled{opacity:.5;cursor:not-allowed}.opaca-ui-tabs-content{margin-top:1rem;padding:1.25rem;border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);background-color:rgba(0,0,0,0);color:var(--opaca-text)}.opaca-ui-tabs-content[data-state=inactive]{display:none}.opaca-data-detail-sheet{width:100% !important}@media(min-width: 640px){.opaca-data-detail-sheet{width:600px !important;max-width:95vw !important}}.opaca-detail-list{display:flex;flex-direction:column;gap:1rem}.opaca-detail-item-card{background:var(--opaca-surface);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius-lg);overflow:hidden;transition:all var(--opaca-transition);box-shadow:0 1px 3px rgba(0,0,0,.05)}.opaca-detail-item-card:hover{border-color:var(--opaca-border-hover);box-shadow:0 4px 12px rgba(0,0,0,.1)}.opaca-detail-item-header{padding:.75rem 1rem;background:hsla(0,0%,100%,.03);border-bottom:1px solid var(--opaca-border);display:flex;align-items:center}.opaca-detail-item-content{padding:1rem}.opaca-detail-grid{display:flex;flex-direction:column;gap:1.25rem}.opaca-detail-grid.nested{padding:1rem;background:hsla(0,0%,100%,.015);border:1px solid var(--opaca-border);border-radius:var(--opaca-radius);margin-top:.5rem}.opaca-detail-row{display:flex;flex-direction:column;gap:.25rem}.opaca-detail-label{font-size:.75rem;font-weight:600;color:var(--opaca-text-muted);text-transform:uppercase;letter-spacing:.05em}.opaca-detail-value-container{font-size:.9375rem;color:var(--opaca-text)}.opaca-detail-value{white-space:pre-wrap;word-break:break-word}.opaca-badge-interactive{cursor:pointer;transition:all .2s ease}.opaca-badge-interactive:hover{background:var(--opaca-surface) !important;border-color:var(--opaca-primary) !important;color:var(--opaca-primary) !important;transform:translateY(-1px);box-shadow:0 2px 4px rgba(0,0,0,.1)}.opaca-row-field{display:flex;flex-wrap:wrap;gap:1rem;margin-bottom:1.25rem}.opaca-row-field .row-item{flex:1;min-width:0}.opaca-row-field .row-item .opaca-form-group{margin-bottom:0}.opaca-separator{flex-shrink:0;background-color:var(--opaca-border)}.opaca-separator.horizontal{height:1px;width:100%}.opaca-separator.vertical{width:1px;height:100%}.opaca-locale-switcher{display:flex;align-items:center;gap:.5rem;background:var(--opaca-card-bg);padding:.25rem;border-radius:var(--opaca-radius);border:1px solid var(--opaca-border);box-shadow:var(--opaca-shadow-sm)}.opaca-locale-btn{background:none;border:none;padding:.375rem .75rem;border-radius:calc(var(--opaca-radius) - 2px);font-size:.75rem;font-weight:600;color:var(--opaca-text-dim);cursor:pointer;transition:all var(--opaca-transition)}.opaca-locale-btn:hover{color:var(--opaca-text);background:hsla(0,0%,100%,.05)}.opaca-locale-btn.active{background:var(--opaca-accent);color:#fff;box-shadow:0 2px 4px rgba(0,0,0,.2)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opacacms",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "scripts": {
5
5
  "build": "bun run ../../scripts/build.ts && tsc --emitDeclarationOnly",
6
6
  "build:publish": "bun run ../../scripts/build.ts --publish && tsc --emitDeclarationOnly",
@@ -27,6 +27,9 @@
27
27
  "opacacms": "./dist/cli/index.js"
28
28
  },
29
29
  "exports": {
30
+ "./admin.css": {
31
+ "import": "./dist/admin.css"
32
+ },
30
33
  ".": {
31
34
  "types": "./dist/index.d.ts",
32
35
  "import": "./dist/index.js"
@@ -159,5 +162,8 @@
159
162
  "react": "^19.2.4",
160
163
  "react-dom": "^19.2.4",
161
164
  "zod": "^4.3.6"
162
- }
165
+ },
166
+ "files": [
167
+ "dist"
168
+ ]
163
169
  }
package/bun.lock DELETED
@@ -1,34 +0,0 @@
1
- {
2
- "lockfileVersion": 1,
3
- "configVersion": 1,
4
- "workspaces": {
5
- "": {
6
- "name": "opacacms",
7
- "dependencies": {
8
- "hono": "^4.11.4",
9
- "zod": "^4.3.5",
10
- },
11
- "devDependencies": {
12
- "@types/bun": "latest",
13
- },
14
- "peerDependencies": {
15
- "typescript": "^5",
16
- },
17
- },
18
- },
19
- "packages": {
20
- "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
21
-
22
- "@types/node": ["@types/node@25.0.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw=="],
23
-
24
- "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
25
-
26
- "hono": ["hono@4.11.4", "", {}, "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA=="],
27
-
28
- "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
29
-
30
- "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
31
-
32
- "zod": ["zod@4.3.5", "", {}, "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g=="],
33
- }
34
- }
@@ -1,47 +0,0 @@
1
- /* src/admin/ui/styles/accordion.scss */
2
-
3
-
4
- /* src/admin/ui/styles/button.scss */
5
-
6
-
7
- /* src/admin/ui/styles/collapsible.scss */
8
-
9
-
10
- /* src/admin/ui/styles/dialog.scss */
11
-
12
-
13
- /* src/admin/ui/styles/group.scss */
14
-
15
-
16
- /* src/admin/ui/styles/input.scss */
17
-
18
-
19
- /* src/admin/ui/styles/label.scss */
20
-
21
-
22
- /* src/admin/ui/styles/radio-group.scss */
23
-
24
-
25
- /* src/admin/ui/styles/select.scss */
26
-
27
-
28
- /* src/admin/ui/styles/sheet.scss */
29
-
30
-
31
- /* src/admin/ui/styles/tabs.scss */
32
-
33
-
34
- /* src/admin/ui/styles/index.scss */
35
-
36
-
37
- /* src/admin/ui/styles/asset-manager.scss */
38
-
39
-
40
- /* src/admin/ui/styles/lexical.scss */
41
-
42
-
43
- /* src/admin/ui/styles/data-detail.scss */
44
-
45
-
46
- /* src/admin/ui/styles/media-registry.scss */
47
-
@@ -1,47 +0,0 @@
1
- /* src/admin/ui/styles/accordion.scss */
2
-
3
-
4
- /* src/admin/ui/styles/button.scss */
5
-
6
-
7
- /* src/admin/ui/styles/collapsible.scss */
8
-
9
-
10
- /* src/admin/ui/styles/dialog.scss */
11
-
12
-
13
- /* src/admin/ui/styles/group.scss */
14
-
15
-
16
- /* src/admin/ui/styles/input.scss */
17
-
18
-
19
- /* src/admin/ui/styles/label.scss */
20
-
21
-
22
- /* src/admin/ui/styles/radio-group.scss */
23
-
24
-
25
- /* src/admin/ui/styles/select.scss */
26
-
27
-
28
- /* src/admin/ui/styles/sheet.scss */
29
-
30
-
31
- /* src/admin/ui/styles/tabs.scss */
32
-
33
-
34
- /* src/admin/ui/styles/index.scss */
35
-
36
-
37
- /* src/admin/ui/styles/asset-manager.scss */
38
-
39
-
40
- /* src/admin/ui/styles/lexical.scss */
41
-
42
-
43
- /* src/admin/ui/styles/data-detail.scss */
44
-
45
-
46
- /* src/admin/ui/styles/media-registry.scss */
47
-
package/global.d.ts DELETED
@@ -1,11 +0,0 @@
1
- import "hono";
2
- import type { OpacaConfig, User } from "./src/types";
3
- import type { Auth } from "./src/auth/index";
4
-
5
- declare module "hono" {
6
- interface ContextVariableMap {
7
- config: OpacaConfig;
8
- auth: Auth;
9
- user: User;
10
- }
11
- }
@@ -1,63 +0,0 @@
1
- import ky from "ky";
2
-
3
- let client: typeof ky | null = null;
4
- let currentBaseURL: string | null = null;
5
-
6
- export const configureAPI = (serverUrl: string) => {
7
- if (!serverUrl || !serverUrl.startsWith("http")) return;
8
- const url = serverUrl;
9
- const baseURL = url.replace(/\/$/, "");
10
- if (client && currentBaseURL === baseURL) return;
11
-
12
- currentBaseURL = baseURL;
13
- client = ky.create({
14
- prefixUrl: baseURL,
15
- credentials: "include",
16
- retry: 0,
17
- hooks: {
18
- afterResponse: [
19
- async (_request, _options, response) => {
20
- if (response.status === 401 || response.status === 403) {
21
- // Se o auth handler do backend falhar globalmente em qualquer requisição da API (sessão morreu/expirada)
22
- // Lançamos a revalidação da store local para despachar o usuário pra tela de login.
23
- try {
24
- const { $session } = await import("./stores/auth");
25
- const { notify } = await import("./stores/ui");
26
- $session.set(null);
27
- notify("Session expired. Please log in again.", "error");
28
- } catch (e) {}
29
- }
30
- },
31
- ],
32
- },
33
- });
34
- };
35
-
36
- export const getCurrentBaseURL = () => {
37
- if (!currentBaseURL) {
38
- throw new Error("API client not configured. Did you provide a valid serverUrl?");
39
- }
40
- return currentBaseURL as string;
41
- };
42
-
43
- /**
44
- * A proxy for ky that allows us to configure the baseURL late
45
- * (e.g. after we have the serverURL from props).
46
- * @param prop: string
47
- */
48
- export const api = new Proxy({} as typeof ky, {
49
- get(_, prop) {
50
- if (!client) {
51
- // No auto-fallback for web components.
52
- throw new Error("API client not configured. Did you provide a valid serverUrl?");
53
- }
54
-
55
- if (!client) throw new Error("API client not configured");
56
-
57
- const c = client;
58
- if (c) {
59
- return c[prop as keyof typeof ky];
60
- }
61
- return undefined;
62
- },
63
- });
@@ -1,40 +0,0 @@
1
- import { createAuthClient } from "better-auth/client";
2
- import { adminClient } from "better-auth/client/plugins";
3
- import { logger } from "@/utils/logger";
4
-
5
- let client: ReturnType<typeof createAuthClient> | null = null;
6
- let currentURL: string | null = null;
7
-
8
- export const configureAuth = (serverUrl: string) => {
9
- if (!serverUrl || !serverUrl.startsWith("http")) return;
10
- const url = serverUrl;
11
- const baseURL = url.replace(/\/$/, "") + "/api/auth";
12
- if (client && currentURL === baseURL) return;
13
-
14
- currentURL = baseURL;
15
- client = createAuthClient({
16
- baseURL,
17
- autoRefresh: true,
18
- fetchOptions: {
19
- credentials: "include",
20
- },
21
- plugins: [adminClient()],
22
- });
23
- };
24
-
25
- /**
26
- * A proxy for the authClient that allows us to configure it late
27
- * (e.g. after we have the user's config with serverURL).
28
- */
29
- export const authClient = new Proxy({} as ReturnType<typeof createAuthClient>, {
30
- get(_, prop) {
31
- if (!client) {
32
- // No auto-fallback to origin for web components.
33
- // AdminClient will call configureAuth when it has the correct prop.
34
- throw new Error("Auth client not configured. Did you provide a valid serverUrl?");
35
- }
36
- return (client as ReturnType<typeof createAuthClient>)[
37
- prop as keyof ReturnType<typeof createAuthClient>
38
- ];
39
- },
40
- });
@@ -1,179 +0,0 @@
1
- export interface CustomFieldProps<TValue = any, TConfig = Record<string, any>> {
2
- /** The current value of the field from the CMS database */
3
- value: TValue;
4
- /** The full schema configuration of the field */
5
- fieldConfig: TConfig;
6
- /** Whether the field is currently disabled by access control */
7
- disabled: boolean;
8
- /** Whether the field is currently read-only by access control */
9
- readOnly: boolean;
10
- /** A validation error message from the CMS, if any */
11
- error?: string;
12
- /** The current full state of the parent form or row */
13
- parentData?: Record<string, unknown>;
14
- /** Callback to notify the CMS that the data has changed */
15
- onChange: (value: TValue) => void;
16
- }
17
-
18
- export interface CustomFieldAdapter {
19
- /**
20
- * Called exactly once when the field is inserted into the CMS Admin UI.
21
- * Use this to render your React root, Vue app, or Vanilla DOM nodes.
22
- *
23
- * @param container The DOM element acting as the wrapper for your component.
24
- * @param props The initial properties (value, config, onChange) from the CMS.
25
- */
26
- mount: (container: HTMLElement, props: CustomFieldProps) => void;
27
-
28
- /**
29
- * Called whenever the CMS pushes new data to the field (e.g. form resets,
30
- * external value changes).
31
- *
32
- * @param container The same DOM element wrapper.
33
- * @param props The updated properties.
34
- */
35
- update?: (container: HTMLElement, props: CustomFieldProps) => void;
36
-
37
- /**
38
- * Called exactly once when the field is removed from the DOM (e.g., navigating away).
39
- * Use this to unmount your React root or Vue app to prevent memory leaks.
40
- *
41
- * @param container The same DOM element wrapper.
42
- */
43
- unmount?: (container: HTMLElement) => void;
44
- }
45
-
46
- /**
47
- * A built-in utility to register framework-agnostic Custom Web Components for the OpacaCMS Admin UI.
48
- *
49
- * It acts as an adapter, translating the Web Component lifecycle (`connectedCallback`, `disconnectedCallback`,
50
- * property setters) into a simple `mount` / `update` / `unmount` paradigm that can easily wrap React, Vue, Angular,
51
- * or plain Javascript applications.
52
- *
53
- * @example
54
- * ```tsx
55
- * import { defineCustomField } from 'opacacms/admin';
56
- * import { createRoot } from 'react-dom/client';
57
- *
58
- * defineCustomField('my-react-picker', {
59
- * mount: (container, props) => {
60
- * (container as any)._root = createRoot(container);
61
- * (container as any)._root.render(<MyPicker {...props} />);
62
- * },
63
- * update: (container, props) => {
64
- * (container as any)._root.render(<MyPicker {...props} />);
65
- * },
66
- * unmount: (container) => {
67
- * (container as any)._root.unmount();
68
- * }
69
- * });
70
- * ```
71
- *
72
- * @param tagName The tag name to register in the browser (e.g., `opaca-color-picker`). Must contain a hyphen.
73
- * @param adapter The adapter containing `mount`, optional `update`, and optional `unmount` lifecycle methods.
74
- */
75
- export function defineCustomField(tagName: string, adapter: CustomFieldAdapter) {
76
- if (typeof window === "undefined") return;
77
-
78
- if (customElements.get(tagName)) {
79
- console.warn(`[OpacaCMS] Custom field tag "${tagName}" is already defined.`);
80
- return;
81
- }
82
-
83
- class OpacaCustomFieldElement extends HTMLElement {
84
- private _value: any;
85
- private _fieldConfig: any;
86
- private _error?: string;
87
- private _parentData?: Record<string, unknown>;
88
- private _isMounted = false;
89
-
90
- // Helper to abstract the complex CustomEvent away from the user's DX
91
- private handleChange = (newValue: any) => {
92
- this.dispatchEvent(
93
- new CustomEvent("opacachange", {
94
- detail: { value: newValue },
95
- bubbles: true,
96
- composed: true,
97
- }),
98
- );
99
- };
100
-
101
- private getProps(): CustomFieldProps {
102
- return {
103
- value: this._value,
104
- fieldConfig: this._fieldConfig,
105
- disabled: this.getAttribute("data-disabled") === "true",
106
- readOnly: this.getAttribute("data-readonly") === "true",
107
- error: this._error,
108
- parentData: this._parentData,
109
- onChange: this.handleChange,
110
- };
111
- }
112
-
113
- set error(err: string | undefined) {
114
- if (this._error !== err) {
115
- this._error = err;
116
- if (this._isMounted && adapter.update) {
117
- adapter.update(this, this.getProps());
118
- }
119
- }
120
- }
121
-
122
- get error() {
123
- return this._error;
124
- }
125
-
126
- set parentData(data: Record<string, unknown> | undefined) {
127
- this._parentData = data;
128
- if (this._isMounted && adapter.update) {
129
- adapter.update(this, this.getProps());
130
- }
131
- }
132
-
133
- get parentData() {
134
- return this._parentData;
135
- }
136
-
137
- set value(val: any) {
138
- const oldValue = this._value;
139
- this._value = val;
140
- // Only call update if the value actually changed and the component is mounted
141
- if (this._isMounted && adapter.update && oldValue !== val) {
142
- adapter.update(this, this.getProps());
143
- }
144
- }
145
-
146
- get value() {
147
- return this._value;
148
- }
149
-
150
- set fieldConfig(config: any) {
151
- this._fieldConfig = config;
152
- if (this._isMounted && adapter.update) {
153
- adapter.update(this, this.getProps());
154
- }
155
- }
156
-
157
- get fieldConfig() {
158
- return this._fieldConfig;
159
- }
160
-
161
- connectedCallback() {
162
- if (!this._isMounted) {
163
- adapter.mount(this, this.getProps());
164
- this._isMounted = true;
165
- }
166
- }
167
-
168
- disconnectedCallback() {
169
- if (this._isMounted) {
170
- if (adapter.unmount) {
171
- adapter.unmount(this);
172
- }
173
- this._isMounted = false;
174
- }
175
- }
176
- }
177
-
178
- customElements.define(tagName, OpacaCustomFieldElement);
179
- }