@valentinkolb/cloud 0.4.0 → 0.5.0
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/package.json +18 -6
- package/scripts/preload.ts +78 -23
- package/src/_internal/define-app.ts +53 -46
- package/src/api/accounts-entities.ts +4 -0
- package/src/api/admin-core-settings.ts +98 -0
- package/src/api/announcements.ts +131 -0
- package/src/api/auth/schemas.ts +24 -0
- package/src/api/auth.ts +113 -10
- package/src/api/index.ts +7 -2
- package/src/api/me.ts +203 -14
- package/src/api/search/schemas.ts +1 -0
- package/src/api/search.ts +62 -8
- package/src/config/ssr.ts +2 -9
- package/src/contracts/announcements.test.ts +37 -0
- package/src/contracts/announcements.ts +121 -0
- package/src/contracts/app.ts +2 -0
- package/src/contracts/index.ts +3 -2
- package/src/contracts/registry.ts +2 -0
- package/src/contracts/shared.ts +108 -1
- package/src/desktop/index.ts +704 -0
- package/src/desktop/solid.tsx +938 -0
- package/src/server/api/index.ts +1 -1
- package/src/server/api/respond.ts +50 -10
- package/src/server/index.ts +44 -38
- package/src/server/middleware/auth.ts +98 -9
- package/src/server/middleware/index.ts +2 -1
- package/src/server/middleware/settings.ts +26 -0
- package/src/server/services/access.test.ts +197 -0
- package/src/server/services/access.ts +254 -6
- package/src/server/services/index.ts +14 -11
- package/src/server/services/pagination.ts +22 -0
- package/src/server/time.ts +45 -0
- package/src/services/account-lifecycle/index.ts +142 -18
- package/src/services/accounts/app.ts +658 -170
- package/src/services/accounts/authz.test.ts +77 -0
- package/src/services/accounts/authz.ts +22 -0
- package/src/services/accounts/entities.ts +84 -5
- package/src/services/accounts/groups.ts +30 -24
- package/src/services/accounts/model.test.ts +30 -0
- package/src/services/accounts/switching.test.ts +14 -0
- package/src/services/accounts/switching.ts +15 -6
- package/src/services/accounts/users.ts +75 -52
- package/src/services/announcements/index.test.ts +32 -0
- package/src/services/announcements/index.ts +224 -0
- package/src/services/audit/index.test.ts +84 -0
- package/src/services/audit/index.ts +431 -0
- package/src/services/auth-flows/index.ts +9 -2
- package/src/services/auth-flows/ipa.ts +0 -2
- package/src/services/auth-flows/magic-link.ts +3 -2
- package/src/services/auth-flows/password-reset.ts +284 -0
- package/src/services/auth-flows/proxy-return.test.ts +24 -0
- package/src/services/auth-flows/proxy-return.ts +49 -0
- package/src/services/gateway.ts +162 -0
- package/src/services/index.ts +44 -2
- package/src/services/ipa/effective-groups.test.ts +33 -0
- package/src/services/ipa/effective-groups.ts +70 -0
- package/src/services/ipa/profile.ts +45 -3
- package/src/services/ipa/search.ts +3 -5
- package/src/services/ipa/service-account.ts +15 -0
- package/src/services/ipa/sync-planning.test.ts +32 -0
- package/src/services/ipa/sync-planning.ts +22 -0
- package/src/services/ipa/sync.ts +110 -38
- package/src/services/oauth-tokens.ts +104 -0
- package/src/services/postgres.ts +21 -6
- package/src/services/providers/local/auth.test.ts +22 -0
- package/src/services/providers/local/auth.ts +46 -3
- package/src/services/secrets.ts +10 -0
- package/src/services/service-account-credentials.test.ts +210 -0
- package/src/services/service-account-credentials.ts +715 -0
- package/src/services/service-accounts.ts +188 -0
- package/src/services/session/index.ts +7 -8
- package/src/services/settings/app.ts +4 -20
- package/src/services/settings/defaults.ts +64 -22
- package/src/services/settings/store.ts +47 -0
- package/src/services/weather/forecast.ts +40 -7
- package/src/services/webauthn.test.ts +36 -0
- package/src/services/webauthn.ts +384 -0
- package/src/shared/icons.ts +391 -100
- package/src/shared/index.ts +7 -0
- package/src/shared/markdown/extensions/code.ts +38 -1
- package/src/shared/markdown/extensions/images.ts +39 -3
- package/src/shared/markdown/extensions/info-blocks.ts +5 -5
- package/src/shared/markdown/extensions/mark.ts +48 -0
- package/src/shared/markdown/extensions/sub-sup.ts +60 -0
- package/src/shared/markdown/extensions/tables.ts +79 -58
- package/src/shared/markdown/formula.test.ts +1089 -0
- package/src/shared/markdown/formula.ts +1187 -0
- package/src/shared/markdown/index.ts +76 -2
- package/src/shared/mock-cover.ts +130 -0
- package/src/shared/redirect.test.ts +49 -0
- package/src/shared/redirect.ts +52 -0
- package/src/shared/theme.test.ts +24 -0
- package/src/shared/theme.ts +68 -0
- package/src/shared/time.ts +13 -0
- package/src/ssr/AdminLayout.tsx +7 -3
- package/src/ssr/AdminSidebar.tsx +115 -49
- package/src/ssr/AppLaunchpad.island.tsx +176 -0
- package/src/ssr/Footer.island.tsx +3 -8
- package/src/ssr/GlobalAnnouncements.island.tsx +141 -0
- package/src/ssr/GlobalSearchDialog.tsx +545 -117
- package/src/ssr/HotkeysHelpRail.island.tsx +3 -70
- package/src/ssr/Layout.tsx +74 -66
- package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
- package/src/ssr/LayoutHelp.tsx +266 -0
- package/src/ssr/NavMenu.island.tsx +0 -39
- package/src/ssr/ThemeToggleRail.island.tsx +3 -3
- package/src/ssr/TimezoneCookie.island.tsx +23 -0
- package/src/ssr/islands/index.ts +13 -0
- package/src/styles/base-popover.css +5 -2
- package/src/styles/effects.css +87 -6
- package/src/styles/global.css +146 -9
- package/src/styles/input.css +3 -1
- package/src/styles/utilities-buttons.css +133 -27
- package/src/styles/utilities-code-display.css +67 -0
- package/src/styles/utilities-completion.css +223 -0
- package/src/styles/utilities-detail.css +73 -0
- package/src/styles/utilities-feedback.css +16 -15
- package/src/styles/utilities-layout.css +42 -2
- package/src/styles/utilities-markdown-editor.css +472 -0
- package/src/styles/utilities-navigation.css +63 -8
- package/src/styles/utilities-script.css +84 -0
- package/src/styles/utilities-table-tile.css +229 -0
- package/src/types/ambient.d.ts +9 -0
- package/src/ui/completion/behaviors.test.ts +95 -0
- package/src/ui/completion/behaviors.ts +205 -0
- package/src/ui/completion/engine.ts +368 -0
- package/src/ui/completion/index.ts +40 -0
- package/src/ui/completion/overlay.ts +92 -0
- package/src/ui/dialog-core.ts +173 -45
- package/src/ui/filter/FilterChip.tsx +42 -40
- package/src/ui/index.ts +11 -12
- package/src/ui/input/AutocompleteEditor.tsx +656 -0
- package/src/ui/input/CheckboxCard.tsx +91 -0
- package/src/ui/input/Combobox.tsx +375 -0
- package/src/ui/input/DatePicker.tsx +846 -0
- package/src/ui/input/DateTimeInput.tsx +29 -4
- package/src/ui/input/FileDropzone.tsx +116 -0
- package/src/ui/input/IconInput.tsx +116 -0
- package/src/ui/input/ImageInput.tsx +19 -2
- package/src/ui/input/MultiSelectInput.tsx +448 -0
- package/src/ui/input/NumberInput.tsx +417 -61
- package/src/ui/input/SegmentedControl.tsx +2 -2
- package/src/ui/input/Select.tsx +172 -10
- package/src/ui/input/Slider.tsx +3 -4
- package/src/ui/input/Switch.tsx +3 -2
- package/src/ui/input/TemplateEditor.tsx +212 -0
- package/src/ui/input/TextInput.tsx +144 -13
- package/src/ui/input/index.ts +53 -8
- package/src/ui/input/markdown/MarkdownEditor.tsx +774 -0
- package/src/ui/input/markdown/Toolbar.tsx +90 -0
- package/src/ui/input/markdown/actions.ts +233 -0
- package/src/ui/input/markdown/active-formats.ts +94 -0
- package/src/ui/input/markdown/behaviors.ts +193 -0
- package/src/ui/input/markdown/code-zone.ts +23 -0
- package/src/ui/input/markdown/highlight.ts +316 -0
- package/src/ui/layout.ts +22 -0
- package/src/ui/misc/AppOverview.tsx +105 -0
- package/src/ui/misc/AppWorkspace.tsx +607 -0
- package/src/ui/misc/Calendar.tsx +1291 -0
- package/src/ui/misc/Chart.tsx +162 -0
- package/src/ui/misc/CodeDisplay.tsx +54 -0
- package/src/ui/misc/ContextMenu.tsx +2 -2
- package/src/ui/misc/DataTable.tsx +269 -0
- package/src/ui/misc/DockWorkspace.tsx +425 -0
- package/src/ui/misc/Docs.tsx +153 -0
- package/src/ui/misc/Dropdown.tsx +2 -2
- package/src/ui/misc/EntitySearch.tsx +260 -129
- package/src/ui/misc/LinkCard.tsx +14 -2
- package/src/ui/misc/LogEntriesTable.tsx +34 -31
- package/src/ui/misc/Pagination.tsx +31 -12
- package/src/ui/misc/PanelDialog.tsx +109 -0
- package/src/ui/misc/Panes.tsx +873 -0
- package/src/ui/misc/PermissionEditor.tsx +358 -262
- package/src/ui/misc/Placeholder.tsx +40 -0
- package/src/ui/misc/ProgressBar.tsx +1 -1
- package/src/ui/misc/ResourceApiKeys.tsx +260 -0
- package/src/ui/misc/SettingsModal.tsx +150 -0
- package/src/ui/misc/StatCell.tsx +182 -40
- package/src/ui/misc/StatGrid.tsx +149 -0
- package/src/ui/misc/StructuredDataPreview.tsx +107 -0
- package/src/ui/misc/code-highlight.ts +213 -0
- package/src/ui/misc/index.ts +93 -12
- package/src/ui/prompts.tsx +362 -312
- package/src/ui/toast.ts +384 -0
- package/src/ui/widgets/Widget.tsx +12 -4
- package/src/ssr/MoreAppsDropdown.island.tsx +0 -61
- package/src/ui/ipa/GroupView.tsx +0 -36
- package/src/ui/ipa/LoginBtn.tsx +0 -16
- package/src/ui/ipa/UserView.tsx +0 -58
- package/src/ui/ipa/index.ts +0 -4
- package/src/ui/navigation.ts +0 -32
- package/src/ui/sidebar.tsx +0 -468
- /package/src/ui/{ipa → misc}/Avatar.tsx +0 -0
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
@apply rounded-lg transition-colors duration-150;
|
|
20
20
|
@apply bg-white border border-zinc-100;
|
|
21
21
|
@apply dark:bg-zinc-900 dark:border-zinc-800;
|
|
22
|
+
/* Gentle inset bevel (clip-safe). Flows through the token so the Layout
|
|
23
|
+
header's inline `box-shadow: var(--theme-shadow-elevated)` stays consistent. */
|
|
24
|
+
box-shadow: var(--theme-shadow-elevated);
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
@utility dialog-panel {
|
|
@@ -27,11 +30,19 @@
|
|
|
27
30
|
@apply backdrop:bg-black/40;
|
|
28
31
|
@apply rounded-lg bg-white dark:bg-zinc-900;
|
|
29
32
|
@apply border border-zinc-200 dark:border-zinc-800;
|
|
30
|
-
|
|
33
|
+
/* Floating dialog (portaled) — outer shadow is fine here. */
|
|
34
|
+
box-shadow: var(--theme-shadow-float);
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
@utility paper-highlighted {
|
|
34
|
-
@apply paper
|
|
38
|
+
@apply paper;
|
|
39
|
+
background: linear-gradient(90deg, rgb(239 246 255 / 0.34), rgb(255 255 255 / 0.82)), var(--color-white);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.dark .paper-highlighted,
|
|
43
|
+
.dark .hover\:paper-highlighted:hover {
|
|
44
|
+
background: rgb(23 37 84 / 0.2);
|
|
45
|
+
box-shadow: var(--theme-shadow-elevated);
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
@utility no-scrollbar {
|
|
@@ -43,6 +54,35 @@
|
|
|
43
54
|
scrollbar-width: none;
|
|
44
55
|
}
|
|
45
56
|
|
|
57
|
+
.panes-tab-strip {
|
|
58
|
+
scrollbar-width: thin;
|
|
59
|
+
scrollbar-color: transparent transparent;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.panes-tab-strip::-webkit-scrollbar-thumb {
|
|
63
|
+
background: transparent;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.panes-tab-strip:hover,
|
|
67
|
+
.panes-tab-strip:focus-within {
|
|
68
|
+
scrollbar-color: rgb(161 161 170 / 0.45) transparent;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.panes-tab-strip:hover::-webkit-scrollbar-thumb,
|
|
72
|
+
.panes-tab-strip:focus-within::-webkit-scrollbar-thumb {
|
|
73
|
+
background: rgb(161 161 170 / 0.45);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.dark .panes-tab-strip:hover,
|
|
77
|
+
.dark .panes-tab-strip:focus-within {
|
|
78
|
+
scrollbar-color: rgb(82 82 91 / 0.6) transparent;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.dark .panes-tab-strip:hover::-webkit-scrollbar-thumb,
|
|
82
|
+
.dark .panes-tab-strip:focus-within::-webkit-scrollbar-thumb {
|
|
83
|
+
background: rgb(82 82 91 / 0.6);
|
|
84
|
+
}
|
|
85
|
+
|
|
46
86
|
@utility ellipsis {
|
|
47
87
|
@apply overflow-hidden text-ellipsis whitespace-nowrap;
|
|
48
88
|
}
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/* Markdown editor — overtype-style layered textarea + preview.
|
|
2
|
+
|
|
3
|
+
The textarea has `color: transparent` so its glyphs are invisible; only
|
|
4
|
+
the cursor (caret-color) and selection show through. The preview div
|
|
5
|
+
beneath renders the same text with markdown syntax highlighted. Identical
|
|
6
|
+
font / padding / whitespace / wrapping on both layers is REQUIRED for
|
|
7
|
+
the cursor to line up exactly with the visible glyphs in the preview.
|
|
8
|
+
|
|
9
|
+
All glyph-affecting properties on `.md-editor-layer` use `!important` to
|
|
10
|
+
defeat any cascading style that would shift the layers relative to each
|
|
11
|
+
other. No font-size, font-weight, or font-style is ever varied between
|
|
12
|
+
the textarea and inner preview spans — bold uses a text-shadow trick,
|
|
13
|
+
italic uses colour. Any real weight/style change risks glyph-width
|
|
14
|
+
drift even on nominally monospace fonts. */
|
|
15
|
+
|
|
16
|
+
.md-editor {
|
|
17
|
+
position: relative;
|
|
18
|
+
@apply rounded-md border;
|
|
19
|
+
@apply bg-zinc-100 border-zinc-100;
|
|
20
|
+
@apply hover:bg-zinc-200/70 hover:border-zinc-200/70;
|
|
21
|
+
@apply dark:bg-zinc-800 dark:border-zinc-800;
|
|
22
|
+
@apply dark:hover:bg-zinc-700 dark:hover:border-zinc-700;
|
|
23
|
+
@apply transition-colors;
|
|
24
|
+
@apply overflow-hidden;
|
|
25
|
+
/* Recessed well — matches the `input` utility (default variant is field-shaped).
|
|
26
|
+
The `paper` variant below overrides this with a card bevel instead. */
|
|
27
|
+
box-shadow: var(--theme-recess);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.md-editor:focus-within {
|
|
31
|
+
@apply bg-white border-blue-400;
|
|
32
|
+
@apply dark:bg-zinc-900 dark:border-blue-400;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.md-editor[data-variant="paper"] {
|
|
36
|
+
@apply rounded-lg;
|
|
37
|
+
@apply bg-white border-zinc-100;
|
|
38
|
+
@apply hover:bg-white hover:border-zinc-100;
|
|
39
|
+
@apply dark:bg-zinc-900 dark:border-zinc-800;
|
|
40
|
+
@apply dark:hover:bg-zinc-900 dark:hover:border-zinc-800;
|
|
41
|
+
/* Paper variant = a document surface, so it gets the card bevel (raised),
|
|
42
|
+
not the recess of the field variant. */
|
|
43
|
+
box-shadow: var(--theme-shadow-elevated);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.md-editor[data-variant="paper"]:focus-within {
|
|
47
|
+
@apply bg-white border-blue-400;
|
|
48
|
+
@apply dark:bg-zinc-900 dark:border-blue-400;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.md-editor[data-variant="paper"] .md-editor-toolbar {
|
|
52
|
+
@apply bg-white border-zinc-100;
|
|
53
|
+
@apply dark:bg-zinc-900 dark:border-zinc-800;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.md-editor[data-variant="paper"] .md-editor-tool {
|
|
57
|
+
@apply hover:bg-zinc-100;
|
|
58
|
+
@apply dark:hover:bg-zinc-800;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.md-editor[data-disabled="true"] {
|
|
62
|
+
@apply opacity-50 cursor-not-allowed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Error state — red border, both unfocused and focused, mirroring how
|
|
66
|
+
the standard `.input` utility would visually signal an error. The
|
|
67
|
+
error MESSAGE itself is rendered separately by `InputWrapper` below
|
|
68
|
+
the editor. */
|
|
69
|
+
.md-editor[data-error="true"],
|
|
70
|
+
.md-editor[data-error="true"]:focus-within {
|
|
71
|
+
@apply border-red-400 dark:border-red-500;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Toolbar bar above the editor surface */
|
|
75
|
+
.md-editor-toolbar {
|
|
76
|
+
@apply flex items-center gap-0.5 flex-wrap;
|
|
77
|
+
@apply px-1.5 py-1;
|
|
78
|
+
@apply border-b border-zinc-200 dark:border-zinc-700;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.md-editor:focus-within .md-editor-toolbar {
|
|
82
|
+
@apply border-zinc-200 dark:border-zinc-700;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.md-editor-tool {
|
|
86
|
+
@apply inline-flex items-center justify-center;
|
|
87
|
+
@apply w-7 h-7 rounded;
|
|
88
|
+
@apply text-zinc-500 dark:text-zinc-400;
|
|
89
|
+
@apply hover:bg-zinc-200 hover:text-zinc-800;
|
|
90
|
+
@apply dark:hover:bg-zinc-700 dark:hover:text-zinc-100;
|
|
91
|
+
@apply transition-colors;
|
|
92
|
+
font-size: 0.95rem;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Active formatting at the caret — blue tint + faint blue background
|
|
96
|
+
so the active state is unmistakable at a glance, without becoming
|
|
97
|
+
visually loud. Hover on an active button keeps the blue surface
|
|
98
|
+
tone (not the zinc one) so the colour doesn't flicker. */
|
|
99
|
+
.md-editor-tool[aria-pressed="true"] {
|
|
100
|
+
@apply text-blue-600 bg-blue-50;
|
|
101
|
+
@apply dark:text-blue-400 dark:bg-blue-950/40;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.md-editor-tool[aria-pressed="true"]:hover {
|
|
105
|
+
@apply bg-blue-100 text-blue-700;
|
|
106
|
+
@apply dark:bg-blue-900/40 dark:text-blue-300;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.md-editor-tool-sep {
|
|
110
|
+
@apply w-px h-4 bg-zinc-300 dark:bg-zinc-600 mx-1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* The two stacked layers. The wrapper has explicit height (set inline
|
|
114
|
+
via style="--md-h: ...em") so both layers can size to it via 100%. */
|
|
115
|
+
.md-editor-surface {
|
|
116
|
+
position: relative;
|
|
117
|
+
height: var(--md-h, 8rem);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.md-editor-layer {
|
|
121
|
+
/* These properties MUST be identical on textarea and preview.
|
|
122
|
+
`!important` because user-agent textarea defaults + any stray rule
|
|
123
|
+
could otherwise shift one layer relative to the other.
|
|
124
|
+
|
|
125
|
+
Bold + italic are NOT done via font-weight / font-style on this
|
|
126
|
+
layer — see the rules further down. We rely on text-shadow for
|
|
127
|
+
fake-bold and colour-only for italic so the glyph advance widths
|
|
128
|
+
stay identical between the textarea and the preview. */
|
|
129
|
+
position: absolute !important;
|
|
130
|
+
inset: 0 !important;
|
|
131
|
+
display: block !important;
|
|
132
|
+
|
|
133
|
+
/* Typography — identical glyph metrics on both layers.
|
|
134
|
+
Every property here is locked down so the textarea and preview
|
|
135
|
+
can never disagree on glyph advance widths. The four critical
|
|
136
|
+
subtleties:
|
|
137
|
+
1. `font-synthesis: none` — only-loaded weights (400/500/600
|
|
138
|
+
for IBM Plex Mono) get used. Asking for 700 would synthesise
|
|
139
|
+
a faux-bold, and synthesised bold does NOT preserve mono
|
|
140
|
+
glyph widths.
|
|
141
|
+
2. `font-variant-ligatures: none` + `font-feature-settings`
|
|
142
|
+
disable ligatures (`==` → `≡` etc.) and contextual
|
|
143
|
+
alternates. Both reshape glyph runs and break alignment.
|
|
144
|
+
3. `letter-spacing: 0` (NOT `normal`) — `normal` lets the font
|
|
145
|
+
decide and some shells inject a fractional value.
|
|
146
|
+
4. `text-rendering: geometricPrecision` — disables kerning so
|
|
147
|
+
even non-monospace fallback fonts don't shift glyph X
|
|
148
|
+
positions across layers. */
|
|
149
|
+
/* IBM Plex Mono is shipped via @fontsource (regular + italic + 500
|
|
150
|
+
+ 600 weights — see global.css). Pinning the font here means the
|
|
151
|
+
editor looks identical across OSes and we control the italic
|
|
152
|
+
variant precisely. The bold/heading "weight" is faked via
|
|
153
|
+
text-shadow further down so we never actually request a weight
|
|
154
|
+
the loaded font lacks. The trailing `monospace` is a last-ditch
|
|
155
|
+
fallback if Plex Mono ever fails to load. */
|
|
156
|
+
font-family: "IBM Plex Mono", monospace !important;
|
|
157
|
+
font-size: 0.875rem !important; /* 14px */
|
|
158
|
+
line-height: 1.55 !important;
|
|
159
|
+
font-weight: 400 !important;
|
|
160
|
+
font-style: normal !important;
|
|
161
|
+
font-variant: normal !important;
|
|
162
|
+
font-variant-ligatures: none !important;
|
|
163
|
+
font-variant-numeric: tabular-nums !important;
|
|
164
|
+
font-variant-position: normal !important;
|
|
165
|
+
font-stretch: normal !important;
|
|
166
|
+
font-kerning: none !important;
|
|
167
|
+
font-synthesis: none !important;
|
|
168
|
+
-webkit-font-synthesis: none !important;
|
|
169
|
+
font-feature-settings:
|
|
170
|
+
"liga" 0,
|
|
171
|
+
"clig" 0,
|
|
172
|
+
"calt" 0 !important;
|
|
173
|
+
font-variation-settings: normal !important;
|
|
174
|
+
font-optical-sizing: none !important;
|
|
175
|
+
letter-spacing: 0 !important;
|
|
176
|
+
word-spacing: 0 !important;
|
|
177
|
+
text-align: left !important;
|
|
178
|
+
text-indent: 0 !important;
|
|
179
|
+
text-transform: none !important;
|
|
180
|
+
text-rendering: geometricPrecision !important;
|
|
181
|
+
|
|
182
|
+
/* Box model */
|
|
183
|
+
padding: 0.625rem 0.75rem !important;
|
|
184
|
+
margin: 0 !important;
|
|
185
|
+
border: 0 !important;
|
|
186
|
+
outline: 0 !important;
|
|
187
|
+
box-sizing: border-box !important;
|
|
188
|
+
|
|
189
|
+
/* Whitespace + wrapping — must agree on every char position */
|
|
190
|
+
white-space: pre-wrap !important;
|
|
191
|
+
word-wrap: break-word !important;
|
|
192
|
+
word-break: normal !important;
|
|
193
|
+
overflow-wrap: break-word !important;
|
|
194
|
+
tab-size: 2 !important;
|
|
195
|
+
-moz-tab-size: 2 !important;
|
|
196
|
+
|
|
197
|
+
/* Sizing */
|
|
198
|
+
width: 100% !important;
|
|
199
|
+
height: 100% !important;
|
|
200
|
+
min-height: 0 !important;
|
|
201
|
+
max-height: none !important;
|
|
202
|
+
min-width: 0 !important;
|
|
203
|
+
max-width: none !important;
|
|
204
|
+
|
|
205
|
+
/* Scrolling — both layers must reserve scrollbar space identically
|
|
206
|
+
or the content widths diverge and long lines wrap at different
|
|
207
|
+
columns. We force `overflow-y: scroll` on both so the gutter is
|
|
208
|
+
always present. The textarea shows its native scrollbar; the
|
|
209
|
+
preview's scrollbar is rendered but painted transparent (see
|
|
210
|
+
`.md-editor-preview` block) — the gutter stays, the visual goes. */
|
|
211
|
+
overflow-y: scroll !important;
|
|
212
|
+
overflow-x: hidden !important;
|
|
213
|
+
scrollbar-width: thin !important;
|
|
214
|
+
|
|
215
|
+
/* Background neutralised — wrapper owns the surface color */
|
|
216
|
+
background: transparent !important;
|
|
217
|
+
|
|
218
|
+
/* No animations that could shift glyph position */
|
|
219
|
+
animation: none !important;
|
|
220
|
+
transition: none !important;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.md-editor-input {
|
|
224
|
+
z-index: 2 !important;
|
|
225
|
+
resize: none !important;
|
|
226
|
+
appearance: none !important;
|
|
227
|
+
-webkit-appearance: none !important;
|
|
228
|
+
-moz-appearance: none !important;
|
|
229
|
+
/* Hide own glyphs, show only cursor.
|
|
230
|
+
`caret-color` must be an explicit colour, NOT `currentColor` — the
|
|
231
|
+
textarea's own `color` is `transparent`, so `currentColor` would
|
|
232
|
+
resolve to transparent and the cursor would vanish. We mirror the
|
|
233
|
+
preview's prose colour (zinc-900 light / zinc-100 dark). */
|
|
234
|
+
color: transparent !important;
|
|
235
|
+
-webkit-text-fill-color: transparent !important;
|
|
236
|
+
caret-color: #18181b !important; /* zinc-900 */
|
|
237
|
+
/* Prevent mobile double-tap zoom */
|
|
238
|
+
touch-action: manipulation !important;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.dark .md-editor-input {
|
|
242
|
+
caret-color: #f4f4f5 !important; /* zinc-100 */
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Selection background visible even with transparent glyphs */
|
|
246
|
+
.md-editor-input::selection {
|
|
247
|
+
background: rgb(59 130 246 / 0.35);
|
|
248
|
+
color: transparent;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.md-editor-preview {
|
|
252
|
+
z-index: 1 !important;
|
|
253
|
+
/* Click + selection happen on the textarea, not here */
|
|
254
|
+
pointer-events: none !important;
|
|
255
|
+
user-select: none !important;
|
|
256
|
+
-webkit-user-select: none !important;
|
|
257
|
+
@apply text-zinc-900 dark:text-zinc-100;
|
|
258
|
+
/* Paint the preview's scrollbar transparent so only the textarea's
|
|
259
|
+
scrollbar (sitting on top, z-index: 2) is visible. We CAN'T set
|
|
260
|
+
`scrollbar-width: none` here — that would also remove the gutter
|
|
261
|
+
and the preview's content width would no longer match the
|
|
262
|
+
textarea's, breaking wrap alignment. The transparent-colour trick
|
|
263
|
+
preserves the gutter while hiding the visual. */
|
|
264
|
+
scrollbar-color: transparent transparent !important;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.md-editor-preview::-webkit-scrollbar-thumb,
|
|
268
|
+
.md-editor-preview::-webkit-scrollbar-track,
|
|
269
|
+
.md-editor-preview::-webkit-scrollbar-corner {
|
|
270
|
+
background: transparent !important;
|
|
271
|
+
border: 0 !important;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* Critical alignment safeguard (overtype lesson, styles.js:256-264):
|
|
275
|
+
force EVERY descendant inside the preview to inherit font-family,
|
|
276
|
+
font-size, and line-height from the preview itself. Without this,
|
|
277
|
+
external CSS (Tailwind base, @tailwindcss/typography, etc.) can
|
|
278
|
+
set element-specific font-size on, say, `<span>` or implicit
|
|
279
|
+
`<h2>` elements. Any such per-element metric difference inflates
|
|
280
|
+
the line-box height and drifts the overlay relative to the
|
|
281
|
+
textarea — visible as a "cursor far to the right of the visible
|
|
282
|
+
text" symptom. Also reset margin/padding/border so no inner
|
|
283
|
+
element can add box-model space the textarea doesn't have. */
|
|
284
|
+
.md-editor-preview *,
|
|
285
|
+
.md-editor-preview *::before,
|
|
286
|
+
.md-editor-preview *::after {
|
|
287
|
+
font-family: inherit !important;
|
|
288
|
+
font-size: inherit !important;
|
|
289
|
+
line-height: inherit !important;
|
|
290
|
+
font-stretch: inherit !important;
|
|
291
|
+
letter-spacing: inherit !important;
|
|
292
|
+
word-spacing: inherit !important;
|
|
293
|
+
margin: 0 !important;
|
|
294
|
+
padding: 0 !important;
|
|
295
|
+
border: 0 !important;
|
|
296
|
+
text-indent: 0 !important;
|
|
297
|
+
vertical-align: baseline !important;
|
|
298
|
+
display: inline !important;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* Placeholder shim — shown when the textarea is empty. MUST use the
|
|
302
|
+
exact same typography stack as `.md-editor-layer` so the cursor
|
|
303
|
+
position doesn't visibly jump when the first character is typed
|
|
304
|
+
and the placeholder fades out. */
|
|
305
|
+
.md-editor-placeholder {
|
|
306
|
+
position: absolute !important;
|
|
307
|
+
inset: 0 !important;
|
|
308
|
+
padding: 0.625rem 0.75rem !important;
|
|
309
|
+
font-family: "IBM Plex Mono", monospace !important;
|
|
310
|
+
font-size: 0.875rem !important;
|
|
311
|
+
line-height: 1.55 !important;
|
|
312
|
+
font-weight: 400 !important;
|
|
313
|
+
letter-spacing: 0 !important;
|
|
314
|
+
word-spacing: 0 !important;
|
|
315
|
+
pointer-events: none !important;
|
|
316
|
+
user-select: none !important;
|
|
317
|
+
z-index: 0 !important;
|
|
318
|
+
@apply text-zinc-400 dark:text-zinc-500;
|
|
319
|
+
white-space: pre-wrap !important;
|
|
320
|
+
word-wrap: break-word !important;
|
|
321
|
+
word-break: normal !important;
|
|
322
|
+
overflow-wrap: break-word !important;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* Syntax highlighting classes — applied INSIDE the preview div by the
|
|
326
|
+
highlighter. Each rule must preserve glyph width (monospace font does
|
|
327
|
+
the heavy lifting; we only change weight, color, background). */
|
|
328
|
+
|
|
329
|
+
/* The visible markdown syntax characters (**, #, -, >, etc.) — kept
|
|
330
|
+
on-screen but dimmed so prose stands out. */
|
|
331
|
+
.md-syntax,
|
|
332
|
+
.md-marker {
|
|
333
|
+
@apply text-zinc-400 dark:text-zinc-500;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Bold + headers via `text-shadow` instead of `font-weight`.
|
|
337
|
+
|
|
338
|
+
Why: any font-weight change risks glyph-advance-width drift between
|
|
339
|
+
the textarea (locked at weight 400) and the preview. Even nominal
|
|
340
|
+
monospace fonts can have subtle weight-dependent width changes,
|
|
341
|
+
especially in browser rasterisers. `text-shadow: 0.5px 0 0
|
|
342
|
+
currentColor` paints a copy of each glyph 0.5px to the right —
|
|
343
|
+
visually equivalent to a heavier stroke, but the underlying glyph
|
|
344
|
+
advance is UNCHANGED. The cursor in the textarea (running at
|
|
345
|
+
weight 400) lines up perfectly with the visibly "bold" glyphs in
|
|
346
|
+
the preview because they ARE rendered at weight 400 underneath.
|
|
347
|
+
|
|
348
|
+
Italics: same logic — `font-style: italic` swaps in a different
|
|
349
|
+
font face whose glyph metrics may not perfectly match upright. We
|
|
350
|
+
simulate italic via colour + a transparent overlay we can't get
|
|
351
|
+
in CSS, so for italic we accept a colour-only differentiation. */
|
|
352
|
+
.md-h1,
|
|
353
|
+
.md-h2,
|
|
354
|
+
.md-h3,
|
|
355
|
+
.md-bold {
|
|
356
|
+
text-shadow: 0.5px 0 0 currentColor;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* Slight tonal ladder so the heading levels are still distinguishable
|
|
360
|
+
at a glance, even though they share the same weight + size. */
|
|
361
|
+
.md-h1 {
|
|
362
|
+
@apply text-zinc-950 dark:text-white;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.md-h2 {
|
|
366
|
+
@apply text-zinc-900 dark:text-zinc-50;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.md-h3 {
|
|
370
|
+
@apply text-zinc-800 dark:text-zinc-200;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* Italic: real `font-style: italic`. IBM Plex Mono Italic 400 is
|
|
374
|
+
loaded as a separate face (see global.css) and is designed as
|
|
375
|
+
monospace — its glyph advance widths match the regular face. The
|
|
376
|
+
`!important` defeats the layer-level `font-style: normal !important`
|
|
377
|
+
so the .md-italic span actually picks up italic. */
|
|
378
|
+
.md-italic {
|
|
379
|
+
font-style: italic !important;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* Inline code — background only, no width change.
|
|
383
|
+
Negative padding would break alignment; we just paint behind the
|
|
384
|
+
glyphs and accept that the highlight doesn't have inset. */
|
|
385
|
+
.md-code {
|
|
386
|
+
@apply bg-zinc-200/60 dark:bg-zinc-700/50 rounded-sm;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* Links — color + underline, both width-safe */
|
|
390
|
+
.md-link {
|
|
391
|
+
@apply text-blue-600 dark:text-blue-400 underline;
|
|
392
|
+
text-underline-offset: 2px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Blockquote — color + inset box-shadow for the left bar (box-shadow
|
|
396
|
+
doesn't affect layout, unlike border-left or padding-left). The bar
|
|
397
|
+
sits visually outside the text column edge so prose stays aligned. */
|
|
398
|
+
.md-quote {
|
|
399
|
+
@apply text-zinc-600 dark:text-zinc-400;
|
|
400
|
+
box-shadow: inset 3px 0 0 var(--color-zinc-300);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.dark .md-quote {
|
|
404
|
+
box-shadow: inset 3px 0 0 var(--color-zinc-600);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* Fenced code lines — background only */
|
|
408
|
+
.md-code-block {
|
|
409
|
+
@apply bg-zinc-200/40 dark:bg-zinc-800;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.md-code-block pre,
|
|
413
|
+
.md-script-source,
|
|
414
|
+
.md-mermaid-block,
|
|
415
|
+
.md-katex-block {
|
|
416
|
+
border: 1px solid rgb(228 228 231 / 0.72) !important;
|
|
417
|
+
border-radius: 0.42rem !important;
|
|
418
|
+
background: color-mix(in oklab, white 92%, rgb(244 244 245)) !important;
|
|
419
|
+
box-shadow:
|
|
420
|
+
inset 0 1px 0 rgb(255 255 255 / 0.46),
|
|
421
|
+
inset 0 -1px 0 rgb(0 0 0 / 0.025);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.md-katex-block {
|
|
425
|
+
padding: 0.75rem;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.dark .md-code-block pre,
|
|
429
|
+
.dark .md-script-source,
|
|
430
|
+
.dark .md-mermaid-block,
|
|
431
|
+
.dark .md-katex-block {
|
|
432
|
+
border-color: rgb(63 63 70 / 0.72) !important;
|
|
433
|
+
background: color-mix(in oklab, rgb(24 24 27) 90%, rgb(39 39 42)) !important;
|
|
434
|
+
box-shadow:
|
|
435
|
+
inset 0 1px 0 rgb(255 255 255 / 0.045),
|
|
436
|
+
inset 0 -1px 0 rgb(0 0 0 / 0.28);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* Horizontal rule (---) — just dim the marker; can't draw an actual
|
|
440
|
+
rule line without inserting layout-shifting elements. */
|
|
441
|
+
.md-hr {
|
|
442
|
+
@apply text-zinc-400 dark:text-zinc-500;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* Completion match — every occurrence of a registered completion
|
|
446
|
+
label (abbreviation or trigger result) is tinted blue so the
|
|
447
|
+
reader can see what's a recognised mention vs. plain prose.
|
|
448
|
+
No underline: a dotted underline reads too much like a spellcheck
|
|
449
|
+
squiggle and trips people into thinking something's broken. */
|
|
450
|
+
.md-completion-match {
|
|
451
|
+
@apply text-blue-600 dark:text-blue-400;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/* Stats footer — lines / words / chars, anchored at the bottom-left
|
|
455
|
+
of the editor. No border, no background tint — it inherits the
|
|
456
|
+
editor body colour and sits as a quiet meta-row separated from the
|
|
457
|
+
content only by its dim mono text and a hair of padding.
|
|
458
|
+
When the editor is empty we keep the element rendered (so the
|
|
459
|
+
editor's outer height stays constant — no layout shift across all
|
|
460
|
+
sibling fields when the user starts / clears typing) but flip
|
|
461
|
+
`visibility: hidden` to remove the stats from sight. */
|
|
462
|
+
.md-editor-stats {
|
|
463
|
+
@apply flex justify-start items-center gap-3;
|
|
464
|
+
@apply px-3 py-1;
|
|
465
|
+
@apply text-[10px] text-zinc-400 dark:text-zinc-500;
|
|
466
|
+
@apply font-mono select-none;
|
|
467
|
+
letter-spacing: 0.02em;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.md-editor-stats[data-empty="true"] {
|
|
471
|
+
visibility: hidden;
|
|
472
|
+
}
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
@utility sidebar {
|
|
20
20
|
@apply flex h-full min-h-0 flex-col gap-3 rounded-lg border border-zinc-200 bg-white;
|
|
21
21
|
@apply dark:border-zinc-800 dark:bg-zinc-900;
|
|
22
|
+
/* Same gentle inset bevel as `paper` so the sidebar reads as a dimensional surface. */
|
|
23
|
+
box-shadow: var(--theme-shadow-elevated);
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
@utility sidebar-container {
|
|
@@ -77,6 +79,43 @@
|
|
|
77
79
|
@apply mt-auto flex flex-col gap-2;
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
@utility sidebar-icon-grid-wrap {
|
|
83
|
+
@apply flex flex-col gap-1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@utility sidebar-icon-grid {
|
|
87
|
+
@apply grid gap-1.5;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@utility sidebar-icon-action {
|
|
91
|
+
@apply flex h-12 w-full min-w-0 items-center justify-center rounded-xl border-0 bg-zinc-100/70 text-zinc-600;
|
|
92
|
+
@apply transition-[background-color,color,transform,box-shadow] duration-150 ease-out hover:bg-zinc-200 hover:text-zinc-900;
|
|
93
|
+
@apply focus-ui;
|
|
94
|
+
@apply active:scale-[0.98];
|
|
95
|
+
@apply dark:bg-zinc-800/65 dark:text-zinc-300 dark:hover:bg-zinc-700 dark:hover:text-zinc-100;
|
|
96
|
+
/* Raised tile (matches the redesign); sinks on press */
|
|
97
|
+
box-shadow: var(--theme-bevel-top), var(--theme-bevel-bottom);
|
|
98
|
+
&:active {
|
|
99
|
+
box-shadow: var(--theme-press);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@utility sidebar-icon-action-active {
|
|
104
|
+
@apply bg-blue-50 text-blue-700 hover:bg-blue-100 hover:text-blue-800;
|
|
105
|
+
@apply dark:bg-blue-950/45 dark:text-blue-300 dark:hover:bg-blue-900/45 dark:hover:text-blue-200;
|
|
106
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-blue-500) 22%, transparent), var(--theme-bevel-top);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@utility sidebar-icon-action-success {
|
|
110
|
+
@apply bg-green-50 text-green-700 hover:bg-green-100 hover:text-green-800;
|
|
111
|
+
@apply dark:bg-green-950/35 dark:text-green-300 dark:hover:bg-green-900/45 dark:hover:text-green-200;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@utility sidebar-icon-action-danger {
|
|
115
|
+
@apply bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-800;
|
|
116
|
+
@apply dark:bg-red-950/35 dark:text-red-300 dark:hover:bg-red-900/45 dark:hover:text-red-200;
|
|
117
|
+
}
|
|
118
|
+
|
|
80
119
|
@utility sidebar-section {
|
|
81
120
|
@apply flex flex-col gap-1;
|
|
82
121
|
}
|
|
@@ -98,6 +137,7 @@
|
|
|
98
137
|
display: flex !important;
|
|
99
138
|
@apply min-h-8 items-center gap-2 rounded-lg px-2 py-1.5 text-xs leading-none text-dimmed;
|
|
100
139
|
@apply transition-[background-color,color] duration-150 ease-out hover:text-secondary;
|
|
140
|
+
@apply focus-ui;
|
|
101
141
|
|
|
102
142
|
&:hover {
|
|
103
143
|
background: var(--theme-list-hover-bg);
|
|
@@ -109,10 +149,17 @@
|
|
|
109
149
|
}
|
|
110
150
|
|
|
111
151
|
@utility sidebar-item-active {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@apply
|
|
115
|
-
@apply
|
|
152
|
+
position: relative;
|
|
153
|
+
background: var(--theme-list-active-bg);
|
|
154
|
+
@apply text-blue-700 font-medium;
|
|
155
|
+
@apply hover:text-blue-700;
|
|
156
|
+
@apply dark:text-blue-300 dark:hover:text-blue-200;
|
|
157
|
+
box-shadow: var(--theme-shadow-elevated);
|
|
158
|
+
|
|
159
|
+
&::before {
|
|
160
|
+
content: "";
|
|
161
|
+
@apply pointer-events-none absolute left-1 top-1/2 h-3.5 w-0.5 -translate-y-1/2 rounded-full bg-blue-500 dark:bg-blue-400;
|
|
162
|
+
}
|
|
116
163
|
}
|
|
117
164
|
|
|
118
165
|
@utility sidebar-item-meta {
|
|
@@ -155,10 +202,17 @@
|
|
|
155
202
|
|
|
156
203
|
@utility list-item-active {
|
|
157
204
|
border-left: var(--theme-list-active-border);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
@apply
|
|
161
|
-
@apply
|
|
205
|
+
position: relative;
|
|
206
|
+
background: var(--theme-list-active-bg);
|
|
207
|
+
@apply rounded-lg text-blue-700 font-medium;
|
|
208
|
+
@apply hover:text-blue-700;
|
|
209
|
+
@apply dark:text-blue-300 dark:hover:text-blue-200;
|
|
210
|
+
box-shadow: var(--theme-shadow-elevated);
|
|
211
|
+
|
|
212
|
+
&::before {
|
|
213
|
+
content: "";
|
|
214
|
+
@apply pointer-events-none absolute left-1 top-1/2 h-3.5 w-0.5 -translate-y-1/2 rounded-full bg-blue-500 dark:bg-blue-400;
|
|
215
|
+
}
|
|
162
216
|
}
|
|
163
217
|
|
|
164
218
|
@utility section-label {
|
|
@@ -181,6 +235,7 @@
|
|
|
181
235
|
border-right: var(--theme-rail-item-active-border-r);
|
|
182
236
|
@apply rounded-lg text-blue-600 dark:text-blue-300;
|
|
183
237
|
@apply bg-blue-50 dark:bg-blue-950;
|
|
238
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-blue-500) 22%, transparent), var(--theme-bevel-top);
|
|
184
239
|
}
|
|
185
240
|
|
|
186
241
|
@utility bottom-bar-item {
|