opacacms 0.1.11 → 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.
- package/dist/admin/index.js +9464 -21
- package/dist/admin/webcomponent.d.ts +1 -1
- package/dist/admin/webcomponent.js +9620 -6
- package/dist/admin.css +1 -0
- package/dist/{chunk-6dhs73zq.js → chunk-0am1m47g.js} +1 -1
- package/dist/{chunk-0nf7fe26.js → chunk-0d9aqz6z.js} +1 -1
- package/dist/{chunk-cvdd4eqh.js → chunk-2kyhqvhc.js} +5 -1
- package/dist/{chunk-gjjcc4hm.js → chunk-2z8wxx9g.js} +21 -6
- package/dist/{chunk-xg35h5a3.js → chunk-7fyepksb.js} +1 -1
- package/dist/{chunk-njytmdb4.js → chunk-pxh5encs.js} +34 -24
- package/dist/{chunk-n8aekdnr.js → chunk-qkn1ykrj.js} +33 -23
- package/dist/{chunk-kwp83w8b.js → chunk-wmvjvn7b.js} +4 -4
- package/dist/{chunk-qrt22f6e.js → chunk-wq314kkx.js} +35 -25
- package/dist/{chunk-eqtsfyjf.js → chunk-x2ejaftz.js} +52 -28
- package/dist/{chunk-6ew02s0c.js → chunk-xtwc125q.js} +18 -18
- package/dist/cli/index.js +5 -5
- package/dist/db/better-sqlite.d.ts +1 -0
- package/dist/db/better-sqlite.js +3 -3
- package/dist/db/bun-sqlite.d.ts +1 -0
- package/dist/db/bun-sqlite.js +3 -3
- package/dist/db/d1.js +3 -3
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.js +17 -13
- package/dist/db/postgres.js +3 -3
- package/dist/db/sqlite.js +3 -3
- package/dist/runtimes/bun.js +2 -2
- package/dist/runtimes/cloudflare-workers.js +2 -2
- package/dist/runtimes/next.js +2 -2
- package/dist/runtimes/node.js +2 -2
- package/dist/server.js +2 -2
- package/package.json +8 -2
- package/bun.lock +0 -34
- package/dist/admin/index.css +0 -47
- package/dist/api.d.ts +0 -6
- package/dist/api.js +0 -27
- package/dist/chunk-2zm8cy1w.js +0 -9482
- package/global.d.ts +0 -11
- package/src/admin/api-client.ts +0 -63
- package/src/admin/auth-client.ts +0 -40
- package/src/admin/custom-field.ts +0 -179
- package/src/admin/index.ts +0 -15
- package/src/admin/react.tsx +0 -72
- package/src/admin/router.ts +0 -9
- package/src/admin/stores/admin-queries.ts +0 -121
- package/src/admin/stores/auth.ts +0 -61
- package/src/admin/stores/column-visibility.ts +0 -67
- package/src/admin/stores/config.ts +0 -15
- package/src/admin/stores/media.ts +0 -95
- package/src/admin/stores/query.ts +0 -13
- package/src/admin/stores/ui.ts +0 -29
- package/src/admin/ui/admin-client.tsx +0 -283
- package/src/admin/ui/admin-layout.tsx +0 -276
- package/src/admin/ui/components/ColumnVisibilityToggle.tsx +0 -141
- package/src/admin/ui/components/DataDetailSheet.tsx +0 -141
- package/src/admin/ui/components/DataDetailView.tsx +0 -175
- package/src/admin/ui/components/Table.tsx +0 -67
- package/src/admin/ui/components/fields/ArrayField.tsx +0 -166
- package/src/admin/ui/components/fields/BlocksField.tsx +0 -202
- package/src/admin/ui/components/fields/BooleanField.tsx +0 -50
- package/src/admin/ui/components/fields/CollapsibleField.tsx +0 -75
- package/src/admin/ui/components/fields/DateField.tsx +0 -45
- package/src/admin/ui/components/fields/FileField.tsx +0 -322
- package/src/admin/ui/components/fields/GroupField.tsx +0 -50
- package/src/admin/ui/components/fields/JoinField.tsx +0 -23
- package/src/admin/ui/components/fields/NumberField.tsx +0 -46
- package/src/admin/ui/components/fields/RadioField.tsx +0 -62
- package/src/admin/ui/components/fields/RelationshipField.tsx +0 -278
- package/src/admin/ui/components/fields/RowField.tsx +0 -40
- package/src/admin/ui/components/fields/SelectField.tsx +0 -59
- package/src/admin/ui/components/fields/TabsField.tsx +0 -101
- package/src/admin/ui/components/fields/TextAreaField.tsx +0 -54
- package/src/admin/ui/components/fields/TextField.tsx +0 -49
- package/src/admin/ui/components/fields/VirtualField.tsx +0 -53
- package/src/admin/ui/components/fields/index.tsx +0 -371
- package/src/admin/ui/components/fields/richtext-editor/index.tsx +0 -211
- package/src/admin/ui/components/fields/richtext-editor/nodes/ImageComponent.tsx +0 -142
- package/src/admin/ui/components/fields/richtext-editor/nodes/ImageNode.tsx +0 -95
- package/src/admin/ui/components/fields/richtext-editor/plugins/ComponentPickerPlugin.tsx +0 -226
- package/src/admin/ui/components/fields/richtext-editor/plugins/EditableSyncPlugin.tsx +0 -16
- package/src/admin/ui/components/fields/richtext-editor/plugins/NotionToolbarPlugin.tsx +0 -184
- package/src/admin/ui/components/fields/richtext-editor/plugins/SimpleToolbarPlugin.tsx +0 -240
- package/src/admin/ui/components/fields/richtext-editor/plugins/ValueSyncPlugin.tsx +0 -40
- package/src/admin/ui/components/fields/utils.ts +0 -1
- package/src/admin/ui/components/link.tsx +0 -41
- package/src/admin/ui/components/media/AssetManagerModal.tsx +0 -334
- package/src/admin/ui/components/toast.tsx +0 -72
- package/src/admin/ui/components/ui/accordion.tsx +0 -51
- package/src/admin/ui/components/ui/alert-dialog.tsx +0 -98
- package/src/admin/ui/components/ui/blocks.tsx +0 -32
- package/src/admin/ui/components/ui/breadcrumbs.tsx +0 -59
- package/src/admin/ui/components/ui/button.tsx +0 -26
- package/src/admin/ui/components/ui/collapsible.tsx +0 -124
- package/src/admin/ui/components/ui/dialog.tsx +0 -79
- package/src/admin/ui/components/ui/group.tsx +0 -20
- package/src/admin/ui/components/ui/index.ts +0 -17
- package/src/admin/ui/components/ui/input.tsx +0 -12
- package/src/admin/ui/components/ui/join.tsx +0 -53
- package/src/admin/ui/components/ui/label.tsx +0 -11
- package/src/admin/ui/components/ui/radio-group.tsx +0 -75
- package/src/admin/ui/components/ui/relationship-detail-sheet.tsx +0 -122
- package/src/admin/ui/components/ui/relationship.tsx +0 -58
- package/src/admin/ui/components/ui/scroll-area.tsx +0 -19
- package/src/admin/ui/components/ui/select.tsx +0 -187
- package/src/admin/ui/components/ui/separator.tsx +0 -21
- package/src/admin/ui/components/ui/sheet.tsx +0 -106
- package/src/admin/ui/components/ui/tabs.tsx +0 -116
- package/src/admin/ui/components/ui/utils.ts +0 -3
- package/src/admin/ui/hooks/use-debounce.ts +0 -15
- package/src/admin/ui/styles/_locale-switcher.scss +0 -33
- package/src/admin/ui/styles/accordion.scss +0 -60
- package/src/admin/ui/styles/animations.scss +0 -41
- package/src/admin/ui/styles/asset-manager.scss +0 -547
- package/src/admin/ui/styles/badge.scss +0 -13
- package/src/admin/ui/styles/base.scss +0 -22
- package/src/admin/ui/styles/button.scss +0 -161
- package/src/admin/ui/styles/card.scss +0 -13
- package/src/admin/ui/styles/collapsible.scss +0 -75
- package/src/admin/ui/styles/data-detail.scss +0 -92
- package/src/admin/ui/styles/dialog.scss +0 -102
- package/src/admin/ui/styles/empty-state.scss +0 -22
- package/src/admin/ui/styles/group.scss +0 -19
- package/src/admin/ui/styles/index.scss +0 -33
- package/src/admin/ui/styles/input.scss +0 -80
- package/src/admin/ui/styles/label.scss +0 -12
- package/src/admin/ui/styles/layout.scss +0 -56
- package/src/admin/ui/styles/lexical.scss +0 -469
- package/src/admin/ui/styles/loading.scss +0 -102
- package/src/admin/ui/styles/media-registry.scss +0 -597
- package/src/admin/ui/styles/pagination.scss +0 -20
- package/src/admin/ui/styles/radio-group.scss +0 -66
- package/src/admin/ui/styles/row.scss +0 -17
- package/src/admin/ui/styles/scrollbar.scss +0 -36
- package/src/admin/ui/styles/select.scss +0 -121
- package/src/admin/ui/styles/separator.scss +0 -14
- package/src/admin/ui/styles/sheet.scss +0 -152
- package/src/admin/ui/styles/sidebar.scss +0 -148
- package/src/admin/ui/styles/switch.scss +0 -59
- package/src/admin/ui/styles/table.scss +0 -207
- package/src/admin/ui/styles/tabs.scss +0 -62
- package/src/admin/ui/styles/toast.scss +0 -45
- package/src/admin/ui/styles/variables.scss +0 -24
- package/src/admin/ui/views/collection-list-view.tsx +0 -720
- package/src/admin/ui/views/dashboard-view.tsx +0 -263
- package/src/admin/ui/views/document-edit-view.tsx +0 -384
- package/src/admin/ui/views/global-edit-view.tsx +0 -226
- package/src/admin/ui/views/init-view.tsx +0 -182
- package/src/admin/ui/views/login-view.tsx +0 -123
- package/src/admin/ui/views/media-registry-view.tsx +0 -1104
- package/src/admin/ui/views/settings-view.tsx +0 -729
- package/src/admin/webcomponent.tsx +0 -15
- package/src/api.ts +0 -9
- package/src/auth/index.ts +0 -194
- package/src/auth/migrations.ts +0 -87
- package/src/auth/premissions.ts +0 -46
- package/src/cli/commands/generate-types.ts +0 -116
- package/src/cli/commands/init.ts +0 -95
- package/src/cli/commands/migrate-commands.ts +0 -160
- package/src/cli/commands/seed-command.ts +0 -11
- package/src/cli/d1-mock.ts +0 -101
- package/src/cli/index.test.ts +0 -84
- package/src/cli/index.ts +0 -183
- package/src/cli/r2-mock.ts +0 -217
- package/src/cli/seeding.ts +0 -409
- package/src/client.ts +0 -181
- package/src/config-utils.ts +0 -102
- package/src/config.ts +0 -49
- package/src/db/adapter.ts +0 -53
- package/src/db/better-sqlite.ts +0 -632
- package/src/db/bun-sqlite.ts +0 -646
- package/src/db/d1.ts +0 -711
- package/src/db/index.ts +0 -6
- package/src/db/kysely/data-mapper.ts +0 -142
- package/src/db/kysely/field-mapper.ts +0 -148
- package/src/db/kysely/migration-generator.ts +0 -223
- package/src/db/kysely/query-builder.ts +0 -92
- package/src/db/kysely/schema-builder.ts +0 -439
- package/src/db/kysely/sql-utils.ts +0 -13
- package/src/db/postgres.ts +0 -621
- package/src/db/sqlite.ts +0 -660
- package/src/db/system-schema.ts +0 -121
- package/src/index.ts +0 -13
- package/src/runtimes/README.md +0 -59
- package/src/runtimes/bun.ts +0 -49
- package/src/runtimes/cloudflare-workers.ts +0 -38
- package/src/runtimes/next.ts +0 -26
- package/src/runtimes/node.ts +0 -52
- package/src/schema/collection.ts +0 -184
- package/src/schema/fields/base.ts +0 -164
- package/src/schema/fields/index.ts +0 -427
- package/src/schema/global.ts +0 -145
- package/src/schema/index.ts +0 -4
- package/src/schema/infer.ts +0 -72
- package/src/server/admin-router.ts +0 -20
- package/src/server/admin.ts +0 -142
- package/src/server/assets.ts +0 -306
- package/src/server/collection-router.ts +0 -55
- package/src/server/handlers.ts +0 -722
- package/src/server/middlewares/admin.ts +0 -27
- package/src/server/middlewares/auth.ts +0 -89
- package/src/server/middlewares/context.ts +0 -17
- package/src/server/middlewares/cors.ts +0 -24
- package/src/server/middlewares/database-init.ts +0 -74
- package/src/server/middlewares/rate-limit.ts +0 -77
- package/src/server/router.ts +0 -47
- package/src/server/setup-middlewares.ts +0 -58
- package/src/server/system-router.ts +0 -35
- package/src/server.ts +0 -9
- package/src/storage/adapters/cloudflare-r2.ts +0 -136
- package/src/storage/adapters/local.ts +0 -146
- package/src/storage/adapters/s3.ts +0 -186
- package/src/storage/errors.ts +0 -46
- package/src/storage/index.ts +0 -5
- package/src/storage/types.ts +0 -39
- package/src/types.ts +0 -577
- package/src/utils/lexical.ts +0 -37
- package/src/utils/logger.ts +0 -73
- package/src/validation.ts +0 -429
- package/src/validator.ts +0 -179
- package/test/admin-custom-field.test.ts +0 -162
- package/test/admin-react-field.test.tsx +0 -134
- package/test/api-features.test.ts +0 -78
- package/test/api.test.ts +0 -178
- package/test/auth.test.ts +0 -62
- package/test/cli-integration.test.ts +0 -148
- package/test/cli.test.ts +0 -25
- package/test/db/postgres.test.ts +0 -95
- package/test/db/sqlite-filter.test.ts +0 -53
- package/test/db/sqlite.test.ts +0 -82
- package/test/engine-features.test.ts +0 -79
- package/test/globals.test.ts +0 -74
- package/test/integration-tmp/db-app/opacacms.config.ts +0 -15
- package/test/integration-tmp/my-sqlite-app/opacacms.config.ts +0 -25
- package/test/integration-tmp/my-test-app/index.ts +0 -8
- package/test/integration-tmp/my-test-app/opacacms.config.ts +0 -16
- package/test/integration-tmp/my-test-app/package.json +0 -12
- package/test/populate.test.ts +0 -79
- package/test/runtimes.test.ts +0 -43
- package/test/schema-builder.test.ts +0 -107
- package/test/schema-features.test.ts +0 -63
- package/test/seeding.test.ts +0 -68
- package/test/storage/local.test.ts +0 -72
- package/test/storage/s3.test.ts +0 -60
- package/test/structural-data.test.ts +0 -100
- package/test/test-setup.ts +0 -11
- package/test/validation.test.ts +0 -162
- package/tsconfig.json +0 -42
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)}
|
|
@@ -17,7 +17,7 @@ async function migrateCreateCommand(opaca, migrationName = "migration", outDir)
|
|
|
17
17
|
fs.mkdirSync(fullMigrationDir, { recursive: true });
|
|
18
18
|
}
|
|
19
19
|
const timestamp = new Date().toISOString().replace(/[-:T]/g, "").split(".")[0];
|
|
20
|
-
const { generateMigrationCode, generateSQLCode } = await import("./chunk-
|
|
20
|
+
const { generateMigrationCode, generateSQLCode } = await import("./chunk-wmvjvn7b.js");
|
|
21
21
|
const { getSystemCollections } = await import("./chunk-v521d72w.js");
|
|
22
22
|
let dialect = "sqlite";
|
|
23
23
|
if (db.name === "postgres")
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// src/db/kysely/field-mapper.ts
|
|
2
2
|
function toSnakeCase(str) {
|
|
3
|
-
|
|
3
|
+
const res = str.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
4
|
+
if (res.startsWith("_") && !str.startsWith("_")) {
|
|
5
|
+
return res.slice(1);
|
|
6
|
+
}
|
|
7
|
+
return res;
|
|
4
8
|
}
|
|
5
9
|
function mapFieldToPostgresType(field) {
|
|
6
10
|
switch (field.type) {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
flattenPayload,
|
|
4
4
|
pushSchema,
|
|
5
5
|
unflattenRow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-xtwc125q.js";
|
|
7
7
|
import {
|
|
8
8
|
BaseDatabaseAdapter
|
|
9
9
|
} from "./chunk-s8mqwnm1.js";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
flattenFields,
|
|
15
15
|
getRelationalFields,
|
|
16
16
|
toSnakeCase
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-2kyhqvhc.js";
|
|
18
18
|
|
|
19
19
|
// src/db/bun-sqlite.ts
|
|
20
20
|
import { Database } from "bun:sqlite";
|
|
@@ -27,6 +27,7 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
27
27
|
_rawDb;
|
|
28
28
|
_db;
|
|
29
29
|
_collections = [];
|
|
30
|
+
_globals = [];
|
|
30
31
|
push;
|
|
31
32
|
migrationDir;
|
|
32
33
|
pushDestructive;
|
|
@@ -342,7 +343,7 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
342
343
|
const block = blocks[i];
|
|
343
344
|
if (!block.blockType)
|
|
344
345
|
continue;
|
|
345
|
-
const blockTableName = `${collection}_${toSnakeCase(key)}_${toSnakeCase(block.blockType)}`.toLowerCase();
|
|
346
|
+
const blockTableName = `${toSnakeCase(collection)}_${toSnakeCase(key)}_${toSnakeCase(block.blockType)}`.toLowerCase();
|
|
346
347
|
const bId = block.id || crypto.randomUUID();
|
|
347
348
|
const blockConfig = blockDef?.blocks?.find((b) => b.slug === block.blockType);
|
|
348
349
|
const blockJsonFields = blockConfig?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
|
|
@@ -470,15 +471,28 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
470
471
|
return row ? unflattenRow(row) : null;
|
|
471
472
|
}
|
|
472
473
|
async updateGlobal(slug, data) {
|
|
474
|
+
const tableName = toSnakeCase(slug);
|
|
473
475
|
const existing = await this.findGlobal(slug);
|
|
474
476
|
const flatData = flattenPayload(data);
|
|
475
|
-
|
|
477
|
+
const globalDef = this._globals.find((g) => g.slug === slug);
|
|
478
|
+
const ts = globalDef?.timestamps !== false;
|
|
479
|
+
if (ts) {
|
|
480
|
+
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
481
|
+
const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
|
|
482
|
+
flatData[updatedField] = new Date().toISOString();
|
|
483
|
+
}
|
|
476
484
|
if (!existing) {
|
|
477
485
|
if (!flatData.id)
|
|
478
486
|
flatData.id = "global";
|
|
479
|
-
|
|
487
|
+
if (ts) {
|
|
488
|
+
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
489
|
+
const createdField = toSnakeCase(config.createdAt || "createdAt");
|
|
490
|
+
if (!flatData[createdField])
|
|
491
|
+
flatData[createdField] = flatData[toSnakeCase(config.updatedAt || "updatedAt")];
|
|
492
|
+
}
|
|
493
|
+
await this._db.insertInto(tableName).values(flatData).execute();
|
|
480
494
|
} else {
|
|
481
|
-
await this._db.updateTable(
|
|
495
|
+
await this._db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
|
|
482
496
|
}
|
|
483
497
|
return this.findGlobal(slug);
|
|
484
498
|
}
|
|
@@ -505,6 +519,7 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
505
519
|
}
|
|
506
520
|
async migrate(collections, globals = []) {
|
|
507
521
|
this._collections = collections;
|
|
522
|
+
this._globals = globals;
|
|
508
523
|
if (this.push) {
|
|
509
524
|
await pushSchema(this._db, "sqlite", collections, globals, {
|
|
510
525
|
pushDestructive: this.pushDestructive
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
flattenPayload,
|
|
4
4
|
pushSchema,
|
|
5
5
|
unflattenRow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-xtwc125q.js";
|
|
7
7
|
import {
|
|
8
8
|
BaseDatabaseAdapter
|
|
9
9
|
} from "./chunk-s8mqwnm1.js";
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "./chunk-62ev8gnc.js";
|
|
13
13
|
import {
|
|
14
14
|
toSnakeCase
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2kyhqvhc.js";
|
|
16
16
|
import {
|
|
17
17
|
__require
|
|
18
18
|
} from "./chunk-8sqjbsgt.js";
|
|
@@ -100,7 +100,8 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
100
100
|
async count(collection, query) {
|
|
101
101
|
if (!this._db)
|
|
102
102
|
throw new Error("DB not connected");
|
|
103
|
-
|
|
103
|
+
const tableName = toSnakeCase(collection);
|
|
104
|
+
let qb = this._db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
|
|
104
105
|
if (query && Object.keys(query).length > 0) {
|
|
105
106
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
106
107
|
}
|
|
@@ -110,6 +111,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
110
111
|
async create(collection, data) {
|
|
111
112
|
if (!this._db)
|
|
112
113
|
throw new Error("DB not connected");
|
|
114
|
+
const tableName = toSnakeCase(collection);
|
|
113
115
|
return this._db.transaction().execute(async (tx) => {
|
|
114
116
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
115
117
|
const jsonFields = colDef?.fields.filter((f) => f.name && (["richtext", "json", "file"].includes(f.type) || f.localized)).map((f) => f.name) || [];
|
|
@@ -139,9 +141,9 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
139
141
|
if (!flatData[updatedField])
|
|
140
142
|
flatData[updatedField] = new Date().toISOString();
|
|
141
143
|
}
|
|
142
|
-
await tx.insertInto(
|
|
144
|
+
await tx.insertInto(tableName).values(flatData).execute();
|
|
143
145
|
for (const [key, values] of Object.entries(hasManyData)) {
|
|
144
|
-
const joinTableName = `${
|
|
146
|
+
const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
|
|
145
147
|
if (values.length > 0) {
|
|
146
148
|
const joinData = values.map((val, idx) => {
|
|
147
149
|
const tId = typeof val === "object" ? val.id : val;
|
|
@@ -155,7 +157,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
155
157
|
const block = blocks[i];
|
|
156
158
|
if (!block.blockType)
|
|
157
159
|
continue;
|
|
158
|
-
const blockTableName = `${
|
|
160
|
+
const blockTableName = `${tableName}_${toSnakeCase(key)}_${block.blockType}`.toLowerCase();
|
|
159
161
|
const bId = block.id || crypto.randomUUID();
|
|
160
162
|
const blockFlatData = flattenPayload({ ...block, id: bId });
|
|
161
163
|
delete blockFlatData.blockType;
|
|
@@ -171,10 +173,11 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
171
173
|
});
|
|
172
174
|
}
|
|
173
175
|
async findOne(collection, query, tx) {
|
|
176
|
+
const tableName = toSnakeCase(collection);
|
|
174
177
|
const executor = tx || this._db;
|
|
175
178
|
if (!executor)
|
|
176
179
|
throw new Error("DB not connected");
|
|
177
|
-
let qb = executor.selectFrom(
|
|
180
|
+
let qb = executor.selectFrom(tableName).selectAll();
|
|
178
181
|
if (query && Object.keys(query).length > 0) {
|
|
179
182
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
180
183
|
}
|
|
@@ -184,7 +187,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
184
187
|
const unflattened = unflattenRow(row);
|
|
185
188
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
186
189
|
if (colDef) {
|
|
187
|
-
const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("./chunk-
|
|
190
|
+
const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("./chunk-7fyepksb.js");
|
|
188
191
|
const relationalFields = getRelationalFields(colDef.fields);
|
|
189
192
|
for (const field of relationalFields) {
|
|
190
193
|
if (!field.name)
|
|
@@ -241,7 +244,8 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
241
244
|
const limit = options?.limit || 10;
|
|
242
245
|
const offset = (page - 1) * limit;
|
|
243
246
|
const total = await this.count(collection, query);
|
|
244
|
-
|
|
247
|
+
const tableName = toSnakeCase(collection);
|
|
248
|
+
let qb = this._db.selectFrom(tableName).selectAll().limit(limit).offset(offset);
|
|
245
249
|
if (query && Object.keys(query).length > 0) {
|
|
246
250
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
247
251
|
}
|
|
@@ -302,11 +306,12 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
302
306
|
const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
|
|
303
307
|
flatData[updatedField] = new Date().toISOString();
|
|
304
308
|
}
|
|
309
|
+
const tableName = toSnakeCase(collection);
|
|
305
310
|
if (Object.keys(flatData).length > 0) {
|
|
306
|
-
await tx.updateTable(
|
|
311
|
+
await tx.updateTable(tableName).set(flatData).where("id", "=", current.id).execute();
|
|
307
312
|
}
|
|
308
313
|
for (const [key, values] of Object.entries(hasManyData)) {
|
|
309
|
-
const joinTableName = `${
|
|
314
|
+
const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
|
|
310
315
|
await tx.deleteFrom(joinTableName).where("source_id", "=", current.id).execute();
|
|
311
316
|
if (values.length > 0) {
|
|
312
317
|
const joinData = values.map((val, idx) => {
|
|
@@ -320,7 +325,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
320
325
|
const fieldDef = colDef?.fields.find((f) => f.name === key);
|
|
321
326
|
if (fieldDef && "blocks" in fieldDef && fieldDef.blocks) {
|
|
322
327
|
for (const b of fieldDef.blocks) {
|
|
323
|
-
const blockTableName = `${
|
|
328
|
+
const blockTableName = `${tableName}_${toSnakeCase(key)}_${b.slug}`.toLowerCase();
|
|
324
329
|
try {
|
|
325
330
|
await tx.deleteFrom(blockTableName).where("_parent_id", "=", current.id).execute();
|
|
326
331
|
} catch (e) {}
|
|
@@ -330,7 +335,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
330
335
|
const block = blocks[i];
|
|
331
336
|
if (!block.blockType)
|
|
332
337
|
continue;
|
|
333
|
-
const blockTableName = `${
|
|
338
|
+
const blockTableName = `${tableName}_${toSnakeCase(key)}_${block.blockType}`.toLowerCase();
|
|
334
339
|
const bId = block.id || crypto.randomUUID();
|
|
335
340
|
const blockFlatData = flattenPayload({ ...block, id: bId });
|
|
336
341
|
delete blockFlatData.blockType;
|
|
@@ -348,8 +353,9 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
348
353
|
async updateMany(collection, query, data) {
|
|
349
354
|
if (!this._db)
|
|
350
355
|
throw new Error("DB not connected");
|
|
356
|
+
const tableName = toSnakeCase(collection);
|
|
351
357
|
return this._db.transaction().execute(async (tx) => {
|
|
352
|
-
let qb = tx.updateTable(
|
|
358
|
+
let qb = tx.updateTable(tableName);
|
|
353
359
|
if (query && Object.keys(query).length > 0) {
|
|
354
360
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
355
361
|
}
|
|
@@ -383,19 +389,20 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
383
389
|
const current = await this.findOne(collection, normalizedQuery);
|
|
384
390
|
if (!current)
|
|
385
391
|
return false;
|
|
392
|
+
const tableName = toSnakeCase(collection);
|
|
386
393
|
await this._db.transaction().execute(async (tx) => {
|
|
387
394
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
388
395
|
if (colDef) {
|
|
389
396
|
for (const field of colDef.fields) {
|
|
390
397
|
const snakeName = toSnakeCase(field.name);
|
|
391
398
|
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
392
|
-
const joinTableName = `${
|
|
399
|
+
const joinTableName = `${tableName}_${snakeName}_relations`.toLowerCase();
|
|
393
400
|
try {
|
|
394
401
|
await tx.deleteFrom(joinTableName).where("source_id", "=", current.id).execute();
|
|
395
402
|
} catch (e) {}
|
|
396
403
|
} else if (field.type === "blocks" && "blocks" in field && field.blocks) {
|
|
397
404
|
for (const b of field.blocks) {
|
|
398
|
-
const blockTableName = `${
|
|
405
|
+
const blockTableName = `${tableName}_${snakeName}_${b.slug}`.toLowerCase();
|
|
399
406
|
try {
|
|
400
407
|
await tx.deleteFrom(blockTableName).where("_parent_id", "=", current.id).execute();
|
|
401
408
|
} catch (e) {}
|
|
@@ -403,15 +410,16 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
403
410
|
}
|
|
404
411
|
}
|
|
405
412
|
}
|
|
406
|
-
await tx.deleteFrom(
|
|
413
|
+
await tx.deleteFrom(tableName).where("id", "=", current.id).execute();
|
|
407
414
|
});
|
|
408
415
|
return true;
|
|
409
416
|
}
|
|
410
417
|
async deleteMany(collection, query) {
|
|
411
418
|
if (!this._db)
|
|
412
419
|
throw new Error("DB not connected");
|
|
420
|
+
const tableName = toSnakeCase(collection);
|
|
413
421
|
return this._db.transaction().execute(async (tx) => {
|
|
414
|
-
let selectQb = tx.selectFrom(
|
|
422
|
+
let selectQb = tx.selectFrom(tableName).select("id");
|
|
415
423
|
if (query && Object.keys(query).length > 0) {
|
|
416
424
|
selectQb = selectQb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
417
425
|
}
|
|
@@ -424,13 +432,13 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
424
432
|
for (const field of colDef.fields) {
|
|
425
433
|
const snakeName = toSnakeCase(field.name);
|
|
426
434
|
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
427
|
-
const joinTableName = `${
|
|
435
|
+
const joinTableName = `${tableName}_${snakeName}_relations`.toLowerCase();
|
|
428
436
|
try {
|
|
429
437
|
await tx.deleteFrom(joinTableName).where("source_id", "in", ids).execute();
|
|
430
438
|
} catch (e) {}
|
|
431
439
|
} else if (field.type === "blocks" && "blocks" in field && field.blocks) {
|
|
432
440
|
for (const b of field.blocks) {
|
|
433
|
-
const blockTableName = `${
|
|
441
|
+
const blockTableName = `${tableName}_${snakeName}_${b.slug}`.toLowerCase();
|
|
434
442
|
try {
|
|
435
443
|
await tx.deleteFrom(blockTableName).where("_parent_id", "in", ids).execute();
|
|
436
444
|
} catch (e) {}
|
|
@@ -438,19 +446,21 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
438
446
|
}
|
|
439
447
|
}
|
|
440
448
|
}
|
|
441
|
-
const result = await tx.deleteFrom(
|
|
449
|
+
const result = await tx.deleteFrom(tableName).where("id", "in", ids).executeTakeFirst();
|
|
442
450
|
return Number(result.numDeletedRows || 0);
|
|
443
451
|
});
|
|
444
452
|
}
|
|
445
453
|
async findGlobal(slug) {
|
|
446
454
|
if (!this._db)
|
|
447
455
|
throw new Error("DB not connected");
|
|
448
|
-
const
|
|
456
|
+
const tableName = toSnakeCase(slug);
|
|
457
|
+
const row = await this._db.selectFrom(tableName).selectAll().limit(1).executeTakeFirst();
|
|
449
458
|
return row ? unflattenRow(row) : null;
|
|
450
459
|
}
|
|
451
460
|
async updateGlobal(slug, data) {
|
|
452
461
|
if (!this._db)
|
|
453
462
|
throw new Error("DB not connected");
|
|
463
|
+
const tableName = toSnakeCase(slug);
|
|
454
464
|
const existing = await this.findGlobal(slug);
|
|
455
465
|
const globalDef = this._globals.find((g) => g.slug === slug);
|
|
456
466
|
const jsonFields = globalDef?.fields.filter((f) => f.name && (["richtext", "json", "file"].includes(f.type) || f.localized)).map((f) => f.name) || [];
|
|
@@ -470,9 +480,9 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
470
480
|
if (!flatData[createdField])
|
|
471
481
|
flatData[createdField] = new Date().toISOString();
|
|
472
482
|
}
|
|
473
|
-
await this._db.insertInto(
|
|
483
|
+
await this._db.insertInto(tableName).values(flatData).execute();
|
|
474
484
|
} else {
|
|
475
|
-
await this._db.updateTable(
|
|
485
|
+
await this._db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
|
|
476
486
|
}
|
|
477
487
|
return this.findGlobal(slug);
|
|
478
488
|
}
|