+ {/* Frame — menu.min.svg as mask, colored by theme */}
+
+
+ {/* Navigation links */}
+
+
+ );
+}
diff --git a/frontend/src/index.css b/frontend/src/index.css
index f8dbc88..a8a09e8 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -6,8 +6,13 @@
@custom-variant dark (&:is(.dark *));
@keyframes romanSlideIn {
- from { transform: translateY(100%); }
- to { transform: translateY(0); }
+ from {
+ transform: translateY(100%);
+ }
+
+ to {
+ transform: translateY(0);
+ }
}
@theme inline {
@@ -162,6 +167,13 @@
--sidebar-ring: oklch(0.72 0.12 230);
}
+/* Terminal-style block cursor — outside @layer so it overrides CodeMirror's theme */
+.cm-cursor,
+.cm-cursor-primary {
+ border-left-color: var(--primary) !important;
+ border-left-width: 0.5em !important;
+}
+
@layer base {
* {
@apply border-border outline-ring/50;
@@ -205,10 +217,7 @@
background: transparent;
}
- /* Terracotta cursor */
- .cm-cursor {
- border-left-color: oklch(0.7 0.13 55) !important;
- }
+
/* Bold console aesthetic */
button,
@@ -315,4 +324,83 @@
input[data-slot="input"] {
border: 2px solid var(--border);
}
+
+ /* ── Floating Nav Menu ── */
+
+ .nav-menu-root {
+ position: relative;
+ width: 200px;
+ height: 400px;
+ will-change: transform;
+ }
+
+ .nav-menu-frame {
+ position: absolute;
+ inset: 0;
+ background: var(--primary);
+ pointer-events: none;
+ mask-size: 100% 100%;
+ mask-repeat: no-repeat;
+ -webkit-mask-size: 100% 100%;
+ -webkit-mask-repeat: no-repeat;
+ }
+
+ .nav-menu-links {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ padding-top: 168px;
+ border: none;
+ }
+
+ .nav-menu-item {
+ width: 100%;
+ text-align: center;
+ padding: 6px 12px;
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--muted-foreground);
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ transition: color 0.2s ease, background 0.2s ease;
+ }
+
+ .nav-menu-item:hover {
+ color: var(--foreground);
+ }
+
+ .nav-menu-item[data-active] {
+ color: var(--primary);
+ background: var(--primary) / 0.08;
+ }
+
+ /* ── Editor Pointer ── */
+
+ .editor-pointer {
+ position: fixed;
+ z-index: 50;
+ pointer-events: none;
+ will-change: top;
+ transform: translateX(-100%);
+ margin-top: -20px;
+ }
+
+ .editor-pointer-img {
+ width: 200px;
+ height: 130px;
+ background: var(--primary);
+ mask-size: contain;
+ mask-repeat: no-repeat;
+ mask-position: center;
+ -webkit-mask-size: contain;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: center;
+ opacity: 1;
+ }
}
\ No newline at end of file
diff --git a/frontend/src/routes/EditorView.tsx b/frontend/src/routes/EditorView.tsx
index 11d3a2e..086eaf6 100644
--- a/frontend/src/routes/EditorView.tsx
+++ b/frontend/src/routes/EditorView.tsx
@@ -13,6 +13,7 @@ import { uframeHighlight } from "@/components/editor/uframeHighlight";
import { uframeCommandSource, uframeValueHintSource, loadCommandsFromApi } from "@/components/editor/uframeCommands";
import { keywordHoverTooltip } from "@/components/editor/uframeHover";
import EditorPane from "@/components/editor/EditorPane";
+import EditorPointer from "@/components/editor/EditorPointer";
import PreviewPane from "@/components/editor/PreviewPane";
import ToolBar from "@/components/editor/ToolBar";
import { EXAMPLES } from "@/components/editor/examples";
@@ -134,6 +135,7 @@ export default function EditorView() {
return (