About Vesper
@@ -436,18 +905,36 @@ function App() {
)}
- {contextMenu && (
-
-
-
-
-
-
-
-
-
+ {selectionRects.length > 0 && (
+
+ {selectionRects.map((r, i) => (
+
+ ))}
)}
+
+
+ {contextMenu && (
+
+ {contextMenu.selectedText && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
);
}
diff --git a/src/main.tsx b/src/main.tsx
index 9de8f43..3589701 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -3,15 +3,8 @@ import ReactDOM from "react-dom/client";
import App from "./App";
import "./styles.css";
-console.log("main.tsx loading...");
-
-try {
- ReactDOM.createRoot(document.getElementById("root")!).render(
-
-
- ,
- );
- console.log("React rendered successfully");
-} catch (e) {
- console.error("Error rendering:", e);
-}
+ReactDOM.createRoot(document.getElementById("root")!).render(
+
+
+ ,
+);
diff --git a/src/styles.css b/src/styles.css
index a261eac..b31a4dd 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1,10 +1,11 @@
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
+@import "@fontsource-variable/inter";
+@import "@fontsource-variable/jetbrains-mono";
@import "tailwindcss";
@plugin "daisyui";
@plugin "@tailwindcss/typography";
@plugin "daisyui/theme" {
- name: "darkmark";
+ name: "vesper";
default: true;
prefersdark: true;
@@ -69,7 +70,7 @@
--color-border-strong: rgba(255, 255, 255, 0.12);
/* Typography */
- font-family: 'Inter', -apple-system, 'SF Pro Display', 'Segoe UI', system-ui, sans-serif;
+ font-family: 'Inter Variable', 'Inter', -apple-system, 'SF Pro Display', 'Segoe UI', system-ui, sans-serif;
font-size: 14px;
line-height: 1.55;
color: var(--color-text-primary);
@@ -239,6 +240,7 @@ html, body, #root {
border-bottom: 1px solid var(--color-border-subtle);
padding: 0 12px;
gap: 2px;
+ overflow: hidden;
}
.menu-item {
@@ -259,6 +261,16 @@ html, body, #root {
color: var(--color-text-primary);
}
+/* Menu backdrop — invisible overlay to catch outside clicks */
+.menu-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 99;
+}
+
/* Menu dropdown */
.menu-dropdown {
position: absolute;
@@ -321,6 +333,52 @@ html, body, #root {
font-weight: 700;
}
+/* Zoom spinner in View menu */
+.menu-dropdown-zoom {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 6px 12px;
+ color: var(--color-text-secondary);
+ font-size: 12px;
+ cursor: default;
+}
+
+.zoom-spinner {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+}
+
+.zoom-spinner-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 22px;
+ height: 22px;
+ background: transparent;
+ border: 1px solid var(--color-border-subtle);
+ border-radius: var(--radius-sm);
+ color: var(--color-text-secondary);
+ cursor: pointer;
+ transition: all 0.15s ease;
+}
+
+.zoom-spinner-btn:hover {
+ background-color: var(--color-bg-hover);
+ color: var(--color-text-primary);
+ border-color: var(--color-text-disabled);
+}
+
+.zoom-spinner-value {
+ min-width: 38px;
+ text-align: center;
+ font-size: 11px;
+ font-weight: 500;
+ font-family: 'SF Mono', 'Menlo', monospace;
+ color: var(--color-text-primary);
+}
+
/* Tab bar */
.tab-bar {
display: flex;
@@ -331,6 +389,32 @@ html, body, #root {
overflow: hidden;
}
+.tab-scroll-arrow {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 100%;
+ background: transparent;
+ border: none;
+ color: var(--color-text-tertiary);
+ cursor: pointer;
+ flex-shrink: 0;
+ transition: all var(--transition-fast);
+ opacity: 0;
+ pointer-events: none;
+}
+
+.tab-scroll-arrow.visible {
+ opacity: 1;
+ pointer-events: auto;
+}
+
+.tab-scroll-arrow:hover {
+ color: var(--color-text-primary);
+ background-color: var(--color-bg-hover);
+}
+
.tab-scroll-container {
display: flex;
align-items: center;
@@ -338,35 +422,30 @@ html, body, #root {
overflow-x: auto;
overflow-y: hidden;
gap: 2px;
- padding: 0 8px;
- scrollbar-width: thin;
+ padding: 0 4px;
+ flex: 1;
+ min-width: 0;
+ scrollbar-width: none;
}
.tab-scroll-container::-webkit-scrollbar {
- height: 4px;
-}
-
-.tab-scroll-container::-webkit-scrollbar-track {
- background: transparent;
-}
-
-.tab-scroll-container::-webkit-scrollbar-thumb {
- background: var(--color-border);
- border-radius: 2px;
+ display: none;
}
.tab {
display: flex;
align-items: center;
- gap: 8px;
+ gap: 6px;
height: 30px;
- padding: 0 14px;
+ padding: 0 12px;
background: transparent;
border: none;
border-radius: var(--radius-md);
color: var(--color-text-secondary);
cursor: pointer;
white-space: nowrap;
+ flex-shrink: 0;
+ overflow: hidden;
transition: all var(--transition-fast);
}
@@ -381,7 +460,6 @@ html, body, #root {
}
.tab-title {
- max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
@@ -400,6 +478,7 @@ html, body, #root {
color: var(--color-text-tertiary);
cursor: pointer;
opacity: 0;
+ flex-shrink: 0;
transition: all var(--transition-fast);
}
@@ -453,8 +532,6 @@ html, body, #root {
min-height: 0;
width: 100%;
padding-bottom: 20px;
- overflow-y: auto;
- overflow-x: hidden;
}
.sidebar-resize-handle {
@@ -485,6 +562,13 @@ html, body, #root {
z-index: 1;
}
+.sidebar-empty {
+ padding: 12px 18px;
+ font-size: 12px;
+ color: var(--color-text-disabled);
+ font-style: italic;
+}
+
.sidebar-item {
display: block;
width: 100%;
@@ -531,11 +615,9 @@ html, body, #root {
}
.content-area-scroll {
- height: 100%;
+ min-height: 100%;
padding: 48px 64px;
box-sizing: border-box;
- overflow-y: auto;
- overflow-x: hidden;
}
/* Search bar */
@@ -614,7 +696,8 @@ html, body, #root {
left: 0;
right: 0;
bottom: 0;
- background-color: rgba(30, 33, 40, 0.92);
+ background-color: rgba(20, 22, 28, 0.88);
+ backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
@@ -625,30 +708,40 @@ html, body, #root {
display: flex;
flex-direction: column;
align-items: center;
- gap: 16px;
- padding: 56px 80px;
- border: 2px dashed var(--color-accent);
- border-radius: var(--radius-lg);
- background-color: var(--color-accent-subtle);
+ gap: 14px;
+ padding: 48px 72px;
+ border: 2px dashed rgba(107, 138, 255, 0.5);
+ border-radius: 16px;
+ background-color: rgba(107, 138, 255, 0.06);
+ box-shadow: 0 0 0 1px rgba(107, 138, 255, 0.1), 0 24px 64px rgba(0, 0, 0, 0.4);
}
-.drop-zone-icon {
- font-size: 48px;
+.drop-zone-icon-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 72px;
+ height: 72px;
+ border-radius: 50%;
+ background: rgba(107, 138, 255, 0.1);
+ color: var(--color-accent);
+ margin-bottom: 4px;
}
.drop-zone-text {
- font-size: 15px;
+ font-size: 16px;
color: var(--color-text-primary);
- font-weight: 500;
+ font-weight: 600;
+ letter-spacing: -0.01em;
}
-/* Focus mode - hide title bar, tabs, and menu, but allow sidebar/search */
-.focus-mode .title-bar,
-.focus-mode .tab-bar,
-.focus-mode .menu-bar {
- display: none !important;
+.drop-zone-hint {
+ font-size: 12px;
+ color: var(--color-text-tertiary);
+ font-weight: 450;
}
+/* Focus mode - animated via framer-motion, extra padding for content */
.focus-mode .content-area-scroll {
padding: 64px 96px;
}
@@ -662,7 +755,7 @@ html, body, #root {
height: 100%;
color: var(--color-text-secondary);
text-align: center;
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
+ font-family: 'Inter Variable', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
}
.welcome-icon {
@@ -768,6 +861,7 @@ html, body, #root {
box-shadow: var(--shadow-modal);
min-width: 360px;
max-width: 480px;
+ width: 90vw;
display: block;
visibility: visible;
opacity: 1;
@@ -778,6 +872,103 @@ html, body, #root {
z-index: 10003 !important;
}
+/* Shortcuts dialog — uses its own class to avoid DaisyUI .modal conflicts */
+.shortcuts-dialog {
+ background-color: var(--color-bg-elevated);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ box-shadow: var(--shadow-modal);
+ width: 640px;
+ max-width: 92vw;
+ max-height: 80vh;
+ overflow-y: auto;
+ position: relative;
+ z-index: 10003;
+}
+
+.shortcuts-dialog-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 24px;
+ border-bottom: 1px solid var(--color-border-subtle);
+}
+
+.shortcuts-dialog-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: var(--color-text-primary);
+}
+
+.shortcuts-dialog-close {
+ width: 28px;
+ height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: transparent;
+ border: none;
+ border-radius: var(--radius-sm);
+ color: var(--color-text-tertiary);
+ cursor: pointer;
+ transition: all var(--transition-fast);
+}
+
+.shortcuts-dialog-close:hover {
+ background-color: var(--color-bg-hover);
+ color: var(--color-text-primary);
+}
+
+.shortcuts-dialog-body {
+ padding: 24px;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 24px 32px;
+}
+
+.shortcuts-dialog-group-title {
+ font-size: 11px;
+ font-weight: 600;
+ color: var(--color-accent);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid var(--color-border-subtle);
+}
+
+.shortcuts-dialog-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 7px 0;
+ font-size: 13px;
+ color: var(--color-text-secondary);
+ gap: 16px;
+}
+
+.shortcuts-dialog-item kbd {
+ font-family: 'JetBrains Mono Variable', 'SF Mono', 'Menlo', monospace;
+ font-size: 11px;
+ padding: 4px 10px;
+ background-color: var(--color-bg-surface);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-sm);
+ color: var(--color-text-primary);
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.shortcuts-dialog-nav-grid {
+ grid-column: 1 / -1;
+}
+
+.shortcuts-dialog-nav-grid .shortcuts-dialog-items-row {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0 32px;
+}
+
.modal-header {
display: flex;
align-items: center;
@@ -873,6 +1064,24 @@ html, body, #root {
margin-bottom: 8px;
}
+.shortcut {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 6px 0;
+ font-size: 13px;
+ color: var(--color-text-secondary);
+}
+
+.shortcut kbd {
+ font-family: 'SF Mono', 'Menlo', monospace;
+ font-size: 11px;
+ padding: 3px 8px;
+ background-color: var(--color-bg-surface);
+ border-radius: var(--radius-sm);
+ color: var(--color-text-primary);
+}
+
.shortcut-item {
display: flex;
align-items: center;
@@ -901,7 +1110,7 @@ html, body, #root {
/* Premium typography settings */
font-size: 17px;
line-height: 1.7;
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
+ font-family: 'Inter Variable', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
font-weight: 400;
letter-spacing: -0.01em;
font-feature-settings: "liga" 1, "kern" 1, "calt" 1;
@@ -913,15 +1122,15 @@ html, body, #root {
text-spacing-trim: trim-both;
}
-/* Refined heading hierarchy */
+/* Refined heading hierarchy — progressive color cascade */
.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
- color: var(--color-text-primary);
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
+ color: #F0F1F3;
+ font-family: 'Inter Variable', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.025em;
@@ -933,7 +1142,8 @@ html, body, #root {
.markdown-content h1 {
font-size: 2.35rem;
font-weight: 700;
- letter-spacing: -0.04em;
+ letter-spacing: -0.03em;
+ color: #F5F6F7;
margin-top: 0;
margin-bottom: 0.8em;
padding-bottom: 0.55em;
@@ -942,6 +1152,7 @@ html, body, #root {
.markdown-content h2 {
font-size: 1.7rem;
+ color: #EDEEF0;
margin-top: 1.85em;
padding-bottom: 0.4em;
border-bottom: 1px solid var(--color-border-subtle);
@@ -949,22 +1160,25 @@ html, body, #root {
.markdown-content h3 {
font-size: 1.3rem;
+ color: #E4E5E8;
}
.markdown-content h4 {
font-size: 1.15rem;
font-weight: 500;
+ color: #D8DADE;
}
.markdown-content h5 {
font-size: 1rem;
font-weight: 500;
+ color: #CBCED3;
}
.markdown-content h6 {
font-size: 0.92rem;
font-weight: 500;
- color: var(--color-text-secondary);
+ color: #ABB0B8;
}
/* Paragraphs with refined spacing */
@@ -974,18 +1188,19 @@ html, body, #root {
line-height: 1.7;
}
-/* Links with subtle interaction */
+/* Links with subtle persistent underline */
.markdown-content a {
color: #7CA9F7;
- text-decoration: none;
+ text-decoration: underline;
+ text-decoration-color: rgba(124, 169, 247, 0.3);
text-underline-offset: 3px;
text-decoration-thickness: 1px;
- transition: all 0.2s ease;
+ transition: color 0.2s ease, text-decoration-color 0.2s ease;
}
.markdown-content a:hover {
color: #93B8F9;
- text-decoration: underline;
+ text-decoration-color: rgba(147, 184, 249, 0.7);
text-decoration-thickness: 1.5px;
}
@@ -1023,12 +1238,19 @@ html, body, #root {
list-style-position: outside;
}
-.markdown-content ol li::marker {
+.markdown-content li::marker {
color: #7CA9F7;
+}
+
+.markdown-content ol li::marker {
font-weight: 500;
font-size: 0.9em;
}
+.markdown-content ul li::marker {
+ font-size: 0.7em;
+}
+
/* Fix ALL nested content inside list items - remove extra margins */
.markdown-content li > p {
margin-top: 0;
@@ -1098,18 +1320,17 @@ html, body, #root {
.markdown-content blockquote {
margin: 1.65em 0;
padding: 1.1em 1.4em;
- border-left: 5px solid #7CA9F7;
- background-color: rgba(124, 169, 247, 0.05);
- border-radius: 10px;
- color: #A8ACB0;
+ border-left: 3px solid #7CA9F7;
+ background-color: rgba(124, 169, 247, 0.04);
+ border-radius: 0 8px 8px 0;
+ color: #B0B4B9;
font-style: normal;
line-height: 1.7;
- box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.markdown-content blockquote p {
margin-bottom: 0.5em;
- color: #A8ACB0;
+ color: #B0B4B9;
}
.markdown-content blockquote p:last-child {
@@ -1118,13 +1339,13 @@ html, body, #root {
/* Inline code */
.markdown-content code:not(pre code) {
- background-color: rgba(124, 169, 247, 0.12);
+ background-color: rgba(255, 255, 255, 0.06);
padding: 0.22em 0.48em;
- border-radius: 4px;
- font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
+ border-radius: 5px;
+ font-family: 'JetBrains Mono Variable', 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
font-size: 0.88em;
font-weight: 400;
- color: #F79E7E;
+ color: #E0A88A;
letter-spacing: -0.02em;
}
@@ -1142,8 +1363,8 @@ html, body, #root {
}
.markdown-content pre code {
- font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
- font-size: 13px;
+ font-family: 'JetBrains Mono Variable', 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
+ font-size: 14px;
line-height: 1.7;
color: #E4E6EB;
background: none;
@@ -1257,12 +1478,12 @@ html, body, #root {
background-color: rgba(124, 169, 247, 0.05);
}
-/* Horizontal rule */
+/* Horizontal rule — gradient fade */
.markdown-content hr {
border: none;
height: 1px;
- background-color: var(--color-border);
- margin: 2.2em 0;
+ background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.1), transparent);
+ margin: 2.5em 0;
}
/* Images */
@@ -1335,6 +1556,41 @@ html, body, #root {
border-radius: 8px;
}
+/* Search highlights — high specificity to override Tailwind reset */
+span.search-highlight {
+ background-color: rgba(251, 191, 36, 0.3) !important;
+ color: inherit !important;
+ border-radius: 2px;
+ padding: 1px 0;
+ transition: background-color 0.15s ease, outline-color 0.15s ease;
+}
+
+span.search-highlight.search-highlight-active {
+ background-color: rgba(251, 191, 36, 0.6) !important;
+ outline: 2px solid rgba(251, 191, 36, 0.7);
+ outline-offset: 1px;
+}
+
+/* OverlayScrollbars custom theme */
+.os-theme-dark {
+ --os-handle-bg: rgba(255, 255, 255, 0.12);
+ --os-handle-bg-hover: var(--color-accent);
+ --os-handle-bg-active: var(--color-accent-hover);
+ --os-size: 8px;
+ --os-handle-border-radius: 4px;
+ --os-padding-perpendicular: 2px;
+ --os-padding-axis: 2px;
+ --os-handle-interactive-area-offset: 4px;
+}
+
+.os-theme-dark .os-scrollbar {
+ transition: opacity 0.3s ease;
+}
+
+.os-theme-dark .os-scrollbar-handle {
+ transition: background-color 0.2s ease, opacity 0.2s ease;
+}
+
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
diff --git a/vite.config.ts b/vite.config.ts
index a8960e3..b13ffe4 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -8,6 +8,9 @@ const host = process.env.TAURI_DEV_HOST;
// https://vite.dev/config/
export default defineConfig(async () => ({
plugins: [react(), tailwindcss()],
+ build: {
+ chunkSizeWarningLimit: 1600,
+ },
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//