diff --git a/src/lib.rs b/src/lib.rs index 4f3e2ba..c9456c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,5 +10,6 @@ pub mod prelude; pub mod serialize; pub mod server; pub mod session; +pub mod store; pub mod templates; pub mod kv; diff --git a/src/store.rs b/src/store.rs new file mode 100644 index 0000000..94a5311 --- /dev/null +++ b/src/store.rs @@ -0,0 +1,24 @@ +use anyhow::Result; +use uuid::Uuid; + +use crate::models::{Document, Project}; + +pub type ItemId = Uuid; + +pub enum Item { + Project(Box), + Document(Box), +} + +pub struct Store { +} + +impl Store { + pub fn save(_item: &Item) -> Result<()> { + todo!() + } + + pub fn load(_id: ItemId) -> Result> { + todo!() + } +} diff --git a/static/style.css b/static/style.css index 081fd20..78b8234 100644 --- a/static/style.css +++ b/static/style.css @@ -854,6 +854,15 @@ select { --tw-text-opacity: 1; color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); } + + .menu li > *:not(ul, .menu-title, details, .btn):active, +.menu li > *:not(ul, .menu-title, details, .btn).active, +.menu li > details > summary:active { + --tw-bg-opacity: 1; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); + } } .btn { @@ -1001,7 +1010,48 @@ select { --tw-border-opacity: 0.2; } +.dropdown { + position: relative; + display: inline-block; +} + +.dropdown > *:not(summary):focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.dropdown .dropdown-content { + position: absolute; +} + +.dropdown:is(:not(details)) .dropdown-content { + visibility: hidden; + opacity: 0; + transform-origin: top; + --tw-scale-x: .95; + --tw-scale-y: .95; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 200ms; +} + +.dropdown.dropdown-open .dropdown-content, +.dropdown:not(.dropdown-hover):focus .dropdown-content, +.dropdown:focus-within .dropdown-content { + visibility: visible; + opacity: 1; +} + @media (hover: hover) { + .dropdown.dropdown-hover:hover .dropdown-content { + visibility: visible; + opacity: 1; + } + .btn:hover { --tw-border-opacity: 1; border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); @@ -1064,6 +1114,28 @@ select { border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); } } + + .dropdown.dropdown-hover:hover .dropdown-content { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + + :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { + cursor: pointer; + outline: 2px solid transparent; + outline-offset: 2px; + } + + @supports (color: oklch(0% 0 0)) { + :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); + } + } +} + +.dropdown:is(details) summary::-webkit-details-marker { + display: none; } .label { @@ -1104,11 +1176,70 @@ select { margin-inline-end: -1rem; } +.join .dropdown .join-item:first-child:not(:last-child), + .join *:first-child:not(:last-child) .dropdown .join-item { + border-start-end-radius: inherit; + border-end-end-radius: inherit; +} + .link { cursor: pointer; text-decoration-line: underline; } +.menu { + display: flex; + flex-direction: column; + flex-wrap: wrap; + font-size: 0.875rem; + line-height: 1.25rem; + padding: 0.5rem; +} + +.menu :where(li ul) { + position: relative; + white-space: nowrap; + margin-inline-start: 1rem; + padding-inline-start: 0.5rem; +} + +.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), .menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { + display: grid; + grid-auto-flow: column; + align-content: flex-start; + align-items: center; + gap: 0.5rem; + grid-auto-columns: minmax(auto, max-content) auto max-content; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.menu li.disabled { + cursor: not-allowed; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + color: var(--fallback-bc,oklch(var(--bc)/0.3)); +} + +.menu :where(li > .menu-dropdown:not(.menu-dropdown-show)) { + display: none; +} + +:where(.menu li) { + position: relative; + display: flex; + flex-shrink: 0; + flex-direction: column; + flex-wrap: wrap; + align-items: stretch; +} + +:where(.menu li) .badge { + justify-self: end; +} + .navbar { display: flex; align-items: center; @@ -1403,6 +1534,14 @@ select { } } +.dropdown.dropdown-open .dropdown-content, +.dropdown:focus .dropdown-content, +.dropdown:focus-within .dropdown-content { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .input input { --tw-bg-opacity: 1; background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); @@ -1475,6 +1614,88 @@ select { outline-offset: 2px; } +:where(.menu li:empty) { + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + opacity: 0.1; + margin: 0.5rem 1rem; + height: 1px; +} + +.menu :where(li ul):before { + position: absolute; + bottom: 0.75rem; + inset-inline-start: 0px; + top: 0.75rem; + width: 1px; + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + opacity: 0.1; + content: ""; +} + +.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), +.menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { + border-radius: var(--rounded-btn, 0.5rem); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + text-align: start; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 200ms; + text-wrap: balance; +} + +:where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):is(summary):not(.active, .btn):focus-visible, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):is(summary):not(.active, .btn):focus-visible { + cursor: pointer; + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + outline: 2px solid transparent; + outline-offset: 2px; +} + +.menu li > *:not(ul, .menu-title, details, .btn):active, +.menu li > *:not(ul, .menu-title, details, .btn).active, +.menu li > details > summary:active { + --tw-bg-opacity: 1; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); +} + +.menu :where(li > details > summary)::-webkit-details-marker { + display: none; +} + +.menu :where(li > details > summary):after, +.menu :where(li > .menu-dropdown-toggle):after { + justify-self: end; + display: block; + margin-top: -0.5rem; + height: 0.5rem; + width: 0.5rem; + transform: rotate(45deg); + transition-property: transform, margin-top; + transition-duration: 0.3s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + content: ""; + transform-origin: 75% 75%; + box-shadow: 2px 2px; + pointer-events: none; +} + +.menu :where(li > details[open] > summary):after, +.menu :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { + transform: rotate(225deg); + margin-top: 0; +} + .mockup-phone .display { overflow: hidden; border-radius: 40px; @@ -2245,15 +2466,47 @@ select { position: fixed; } +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.-inset-0 { + inset: -0px; +} + +.-inset-0\.5 { + inset: -0.125rem; +} + +.-inset-1 { + inset: -0.25rem; +} + +.-inset-1\.5 { + inset: -0.375rem; +} + .inset-y-0 { top: 0px; bottom: 0px; } +.right-0 { + right: 0px; +} + .z-50 { z-index: 50; } +.z-10 { + z-index: 10; +} + .-mx-2 { margin-left: -0.5rem; margin-right: -0.5rem; @@ -2264,6 +2517,11 @@ select { margin-right: -1.5rem; } +.mx-auto { + margin-left: auto; + margin-right: auto; +} + .mt-10 { margin-top: 2.5rem; } @@ -2280,14 +2538,42 @@ select { margin-top: auto; } +.-mr-2 { + margin-right: -0.5rem; +} + +.ml-10 { + margin-left: 2.5rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.ml-4 { + margin-left: 1rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + .block { display: block; } +.inline-block { + display: inline-block; +} + .flex { display: flex; } +.inline-flex { + display: inline-flex; +} + .hidden { display: none; } @@ -2308,11 +2594,6 @@ select { height: 100%; } -.h-max { - height: -moz-max-content; - height: max-content; -} - .min-h-full { min-height: 100%; } @@ -2345,15 +2626,26 @@ select { width: 100%; } -.w-max { - width: -moz-max-content; - width: max-content; +.w-48 { + width: 12rem; +} + +.max-w-7xl { + max-width: 80rem; +} + +.max-w-xs { + max-width: 20rem; } .flex-1 { flex: 1 1 0%; } +.flex-shrink-0 { + flex-shrink: 0; +} + .shrink-0 { flex-shrink: 0; } @@ -2362,6 +2654,10 @@ select { flex-grow: 1; } +.origin-top-right { + transform-origin: top right; +} + .flex-col { flex-direction: column; } @@ -2370,6 +2666,10 @@ select { align-items: center; } +.items-baseline { + align-items: baseline; +} + .justify-end { justify-content: flex-end; } @@ -2378,6 +2678,10 @@ select { justify-content: center; } +.justify-between { + justify-content: space-between; +} + .justify-around { justify-content: space-around; } @@ -2420,6 +2724,16 @@ select { margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); } +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} + +.overflow-hidden { + overflow: hidden; +} + .overflow-y-auto { overflow-y: auto; } @@ -2458,6 +2772,10 @@ select { border-right-width: 1px; } +.border-t { + border-top-width: 1px; +} + .border-solid { border-style: solid; } @@ -2467,14 +2785,9 @@ select { border-color: rgb(229 231 235 / var(--tw-border-opacity)); } -.border-black { +.border-purple-700 { --tw-border-opacity: 1; - border-color: rgb(0 0 0 / var(--tw-border-opacity)); -} - -.border-gray-400 { - --tw-border-opacity: 1; - border-color: rgb(156 163 175 / var(--tw-border-opacity)); + border-color: rgb(126 34 206 / var(--tw-border-opacity)); } .bg-accent { @@ -2507,6 +2820,36 @@ select { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } +.bg-emerald-600 { + --tw-bg-opacity: 1; + background-color: rgb(5 150 105 / var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} + +.bg-purple-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 232 255 / var(--tw-bg-opacity)); +} + +.bg-purple-200 { + --tw-bg-opacity: 1; + background-color: rgb(233 213 255 / var(--tw-bg-opacity)); +} + +.bg-purple-600 { + --tw-bg-opacity: 1; + background-color: rgb(147 51 234 / var(--tw-bg-opacity)); +} + +.bg-purple-700 { + --tw-bg-opacity: 1; + background-color: rgb(126 34 206 / var(--tw-bg-opacity)); +} + .p-2 { padding: 0.5rem; } @@ -2551,10 +2894,52 @@ select { padding-bottom: 2rem; } +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} + .pl-72 { padding-left: 18rem; } +.pb-3 { + padding-bottom: 0.75rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pt-4 { + padding-top: 1rem; +} + .text-center { text-align: center; } @@ -2583,6 +2968,11 @@ select { line-height: 1rem; } +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + .font-bold { font-weight: 700; } @@ -2646,6 +3036,21 @@ select { color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); } +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + +.text-purple-200 { + --tw-text-opacity: 1; + color: rgb(233 213 255 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + .shadow { --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); @@ -2664,6 +3069,12 @@ select { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + .ring-1 { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -2679,6 +3090,15 @@ select { --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); } +.ring-black { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity)); +} + +.ring-opacity-5 { + --tw-ring-opacity: 0.05; +} + /* Borrowed from https://github.com/Milkdown/milkdown/blob/main/e2e/src/list-item-block/style.css which is licensed under MIT. */ @@ -2754,6 +3174,20 @@ milkdown-list-item-block .label-wrapper { background-color: rgb(249 250 251 / var(--tw-bg-opacity)); } +.hover\:bg-emerald-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(16 185 129 / var(--tw-bg-opacity)); +} + +.hover\:bg-purple-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(168 85 247 / var(--tw-bg-opacity)); +} + +.hover\:bg-opacity-75:hover { + --tw-bg-opacity: 0.75; +} + .hover\:text-gray-900:hover { --tw-text-opacity: 1; color: rgb(17 24 39 / var(--tw-text-opacity)); @@ -2764,6 +3198,16 @@ milkdown-list-item-block .label-wrapper { color: rgb(79 70 229 / var(--tw-text-opacity)); } +.hover\:text-white:hover { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + .focus\:ring-2:focus { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -2779,6 +3223,40 @@ milkdown-list-item-block .label-wrapper { --tw-ring-color: var(--fallback-a,oklch(var(--a)/var(--tw-ring-opacity))); } +.focus\:ring-emerald-600:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(5 150 105 / var(--tw-ring-opacity)); +} + +.focus\:ring-white:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity)); +} + +.focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; +} + +.focus\:ring-offset-purple-600:focus { + --tw-ring-offset-color: #9333ea; +} + +.focus-visible\:outline:focus-visible { + outline-style: solid; +} + +.focus-visible\:outline-2:focus-visible { + outline-width: 2px; +} + +.focus-visible\:outline-offset-2:focus-visible { + outline-offset: 2px; +} + +.focus-visible\:outline-emerald-600:focus-visible { + outline-color: #059669; +} + .group:hover .group-hover\:border-indigo-600 { --tw-border-opacity: 1; border-color: rgb(79 70 229 / var(--tw-border-opacity)); @@ -2826,6 +3304,11 @@ milkdown-list-item-block .label-wrapper { padding-right: 1.5rem; } + .sm\:px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; + } + .sm\:text-sm { font-size: 0.875rem; line-height: 1.25rem; @@ -2836,6 +3319,20 @@ milkdown-list-item-block .label-wrapper { } } +@media (min-width: 768px) { + .md\:ml-6 { + margin-left: 1.5rem; + } + + .md\:block { + display: block; + } + + .md\:hidden { + display: none; + } +} + @media (min-width: 1024px) { .lg\:px-8 { padding-left: 2rem;