${selectedLibrary ? getLibraryName(libraries, selectedLibrary) : selectedTag ? `#${selectedTag}` : 'All Documents'}
+No documents found
+${searchQuery || selectedTag ? 'Try adjusting your filters' : 'Create your first document'}
+ +diff --git a/package.json b/package.json index a951523..dd5382e 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "express": "^4.21.0", "gray-matter": "^4.0.3", "js-yaml": "^4.1.0", + "marked": "^11.1.0", "uuid": "^9.0.1" }, "devDependencies": { diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..c5ffc92 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,1260 @@ +/* === Design Tokens === */ +:root { + /* Backgrounds */ + --color-bg: #0f1117; + --color-surface: #1a1d26; + --color-surface-raised: #22262f; + --color-hover: rgba(255, 255, 255, 0.05); + --color-active: rgba(255, 255, 255, 0.08); + + /* Text */ + --color-text: #e4e6eb; + --color-text-secondary: #b0b3b8; + --color-text-muted: #65676b; + + /* Borders */ + --color-border: #303338; + --color-border-light: #404249; + + /* Accent */ + --color-accent: #00d4aa; + --color-accent-hover: #00e8bb; + --color-accent-alpha: rgba(0, 212, 170, 0.15); + + /* Semantic */ + --color-success: #31a065; + --color-success-bg: rgba(49, 160, 101, 0.15); + --color-warning: #cf9d2c; + --color-warning-bg: rgba(207, 157, 44, 0.15); + --color-danger: #cf4a4a; + --color-danger-hover: #e05555; + --color-info: #4a90cf; + --color-info-bg: rgba(74, 144, 207, 0.15); + + /* Type colors */ + --color-requirement: #00d4aa; + --color-note: #4a90cf; + --color-spec: #9b59b6; + --color-general: #65676b; + + /* Priority */ + --color-priority-high: #cf4a4a; + --color-priority-medium: #cf9d2c; + --color-priority-low: #31a065; + + /* Shadows */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.25); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.3); + + /* Typography */ + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Consolas', monospace; + + /* Spacing */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.25rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-10: 2.5rem; + + /* Border Radius */ + --radius-sm: 4px; + --radius-md: 6px; + --radius-lg: 8px; + --radius-xl: 12px; + --radius-full: 9999px; + + /* Transitions */ + --transition-fast: 0.1s ease; + --transition-normal: 0.15s ease; + + /* Z-index */ + --z-sidebar: 100; + --z-modal: 500; + --z-toast: 600; +} + +/* === Light Theme === */ +[data-theme="light"] { + --color-bg: #f5f6f8; + --color-surface: #ffffff; + --color-surface-raised: #f0f1f3; + --color-hover: rgba(0, 0, 0, 0.04); + --color-active: rgba(0, 0, 0, 0.06); + --color-text: #1a1d26; + --color-text-secondary: #4a4f5c; + --color-text-muted: #8b919d; + --color-border: #e1e3e8; + --color-border-light: #ebedf2; + --color-accent: #00a884; + --color-accent-hover: #00c49a; + --color-accent-alpha: rgba(0, 168, 132, 0.12); + --color-success: #22863a; + --color-warning: #b08800; + --color-danger: #cb2431; + --color-info: #0366d6; +} + +/* === Reset === */ +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + font-size: 16px; + -webkit-font-smoothing: antialiased; +} + +body { + font-family: var(--font-sans); + font-size: 1rem; + line-height: 1.5; + color: var(--color-text); + background: var(--color-bg); + min-height: 100vh; +} + +a { + color: var(--color-accent); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +button { + font: inherit; + cursor: pointer; + border: none; + background: none; +} + +input, textarea, select { + font: inherit; +} + +ul, ol { + list-style: none; +} + +/* === Scrollbar === */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--color-surface); +} + +::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: var(--radius-full); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-text-muted); +} + +/* === Layout === */ +#app { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +.app-layout { + display: flex; + flex: 1; + overflow: hidden; +} + +/* === Header === */ +.app-header { + display: flex; + align-items: center; + gap: var(--space-4); + padding: var(--space-3) var(--space-4); + background: var(--color-surface); + border-bottom: 1px solid var(--color-border); + height: 56px; +} + +.app-header .logo { + font-weight: 700; + font-size: 1.125rem; + color: var(--color-accent); + display: flex; + align-items: center; + gap: var(--space-2); +} + +.app-header .search-box { + flex: 1; + max-width: 400px; + position: relative; +} + +.app-header .search-box input { + width: 100%; + padding: var(--space-2) var(--space-4); + padding-left: 36px; + background: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + color: var(--color-text); +} + +.app-header .search-box input:focus { + outline: none; + border-color: var(--color-accent); +} + +.app-header .search-box .icon { + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); + color: var(--color-text-muted); +} + +.header-actions { + display: flex; + gap: var(--space-2); + margin-left: auto; +} + +/* === Sidebar === */ +.sidebar { + width: 260px; + background: var(--color-surface); + border-right: 1px solid var(--color-border); + display: flex; + flex-direction: column; + overflow: hidden; + flex-shrink: 0; +} + +.sidebar-section { + padding: var(--space-4); + border-bottom: 1px solid var(--color-border); +} + +.sidebar-section h3 { + font-size: 0.75rem; + font-weight: 600; + color: var(--color-text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: var(--space-3); +} + +.sidebar-scroll { + flex: 1; + overflow-y: auto; + padding: var(--space-4); +} + +.library-tree { + display: flex; + flex-direction: column; + gap: var(--space-1); +} + +.tree-item { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-2) var(--space-3); + border-radius: var(--radius-md); + cursor: pointer; + color: var(--color-text-secondary); + transition: var(--transition-fast); +} + +.tree-item:hover { + background: var(--color-hover); + color: var(--color-text); +} + +.tree-item.active { + background: var(--color-accent-alpha); + color: var(--color-accent); +} + +.tree-item .icon { + flex-shrink: 0; + width: 16px; + height: 16px; +} + +.tree-item .label { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.tree-children { + padding-left: var(--space-4); +} + +.tree-toggle { + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + transition: transform var(--transition-fast); +} + +.tree-toggle.expanded { + transform: rotate(90deg); +} + +.tag-list { + display: flex; + flex-direction: column; + gap: var(--space-1); +} + +.tag-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-2) var(--space-3); + border-radius: var(--radius-md); + cursor: pointer; + color: var(--color-text-secondary); + transition: var(--transition-fast); +} + +.tag-item:hover { + background: var(--color-hover); + color: var(--color-text); +} + +.tag-item.active { + background: var(--color-accent-alpha); + color: var(--color-accent); +} + +.tag-count { + font-size: 0.75rem; + color: var(--color-text-muted); +} + +/* === Main Content === */ +.main-content { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.content-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-4) var(--space-6); + border-bottom: 1px solid var(--color-border); + background: var(--color-surface); +} + +.content-header h1 { + font-size: 1.25rem; + font-weight: 600; +} + +.content-body { + flex: 1; + overflow-y: auto; + padding: var(--space-6); +} + +/* === Document Cards === */ +.doc-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: var(--space-4); +} + +.doc-card { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + padding: var(--space-4); + cursor: pointer; + transition: var(--transition-fast); +} + +.doc-card:hover { + border-color: var(--color-accent); + box-shadow: var(--shadow-md); +} + +.doc-card-header { + display: flex; + align-items: flex-start; + gap: var(--space-3); + margin-bottom: var(--space-3); +} + +.doc-id { + font-family: var(--font-mono); + font-size: 0.75rem; + color: var(--color-text-muted); + background: var(--color-bg); + padding: 2px 6px; + border-radius: var(--radius-sm); +} + +.doc-title { + font-size: 1rem; + font-weight: 600; + color: var(--color-text); + flex: 1; + line-height: 1.4; +} + +.doc-tags { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); + margin-bottom: var(--space-3); +} + +.tag-pill { + font-size: 0.75rem; + padding: 2px 8px; + background: var(--color-accent-alpha); + color: var(--color-accent); + border-radius: var(--radius-full); +} + +.doc-meta { + display: flex; + align-items: center; + gap: var(--space-3); + font-size: 0.75rem; + color: var(--color-text-muted); +} + +.doc-meta-item { + display: flex; + align-items: center; + gap: var(--space-1); +} + +.status-badge { + padding: 2px 8px; + border-radius: var(--radius-full); + font-size: 0.75rem; + font-weight: 500; +} + +.status-badge.draft { + background: var(--color-warning-bg); + color: var(--color-warning); +} + +.status-badge.approved { + background: var(--color-info-bg); + color: var(--color-info); +} + +.status-badge.implemented { + background: var(--color-success-bg); + color: var(--color-success); +} + +.priority-indicator { + font-size: 0.875rem; +} + +/* === Document Viewer === */ +.doc-viewer { + display: flex; + gap: var(--space-6); + max-width: 1200px; +} + +.doc-content { + flex: 1; + min-width: 0; +} + +.doc-sidebar { + width: 280px; + flex-shrink: 0; +} + +.doc-viewer-header { + display: flex; + align-items: center; + gap: var(--space-4); + margin-bottom: var(--space-6); +} + +.back-btn { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-2) var(--space-3); + border-radius: var(--radius-md); + color: var(--color-text-secondary); + transition: var(--transition-fast); +} + +.back-btn:hover { + background: var(--color-hover); + color: var(--color-text); +} + +.doc-actions { + display: flex; + gap: var(--space-2); + margin-left: auto; +} + +.doc-actions button { + padding: var(--space-2) var(--space-3); + border-radius: var(--radius-md); + display: flex; + align-items: center; + gap: var(--space-2); + color: var(--color-text-secondary); + transition: var(--transition-fast); +} + +.doc-actions button:hover { + background: var(--color-hover); + color: var(--color-text); +} + +.doc-actions button.danger:hover { + background: var(--color-danger-hover); + color: white; +} + +.prose { + color: var(--color-text); + line-height: 1.7; +} + +.prose h1 { font-size: 2rem; font-weight: 700; margin: 1.5em 0 0.5em; } +.prose h2 { font-size: 1.5rem; font-weight: 600; margin: 1.5em 0 0.5em; } +.prose h3 { font-size: 1.25rem; font-weight: 600; margin: 1.25em 0 0.5em; } +.prose h4 { font-size: 1rem; font-weight: 600; margin: 1em 0 0.5em; } + +.prose p { margin-bottom: 1em; } + +.prose ul, .prose ol { + margin-bottom: 1em; + padding-left: 1.5em; +} + +.prose ul { list-style-type: disc; } +.prose ol { list-style-type: decimal; } + +.prose li { margin-bottom: 0.25em; } + +.prose code { + font-family: var(--font-mono); + font-size: 0.875em; + background: var(--color-surface); + padding: 0.125em 0.375em; + border-radius: var(--radius-sm); + color: var(--color-accent); +} + +.prose pre { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + padding: 1rem; + overflow-x: auto; + margin-bottom: 1em; +} + +.prose pre code { + background: transparent; + padding: 0; + color: var(--color-text); +} + +.prose blockquote { + border-left: 4px solid var(--color-accent); + padding-left: 1rem; + margin: 1em 0; + color: var(--color-text-secondary); +} + +.prose table { + width: 100%; + border-collapse: collapse; + margin-bottom: 1em; +} + +.prose th, .prose td { + padding: 0.5rem 0.75rem; + border: 1px solid var(--color-border); + text-align: left; +} + +.prose th { + background: var(--color-surface); + font-weight: 600; +} + +.prose input[type="checkbox"] { + margin-right: 0.5em; + accent-color: var(--color-accent); +} + +.prose hr { + border: none; + border-top: 1px solid var(--color-border); + margin: 2em 0; +} + +/* === Metadata Panel === */ +.meta-section { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + margin-bottom: var(--space-4); +} + +.meta-header { + padding: var(--space-3) var(--space-4); + border-bottom: 1px solid var(--color-border); + font-size: 0.75rem; + font-weight: 600; + color: var(--color-text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.meta-body { + padding: var(--space-4); +} + +.meta-row { + display: flex; + justify-content: space-between; + margin-bottom: var(--space-3); +} + +.meta-row:last-child { + margin-bottom: 0; +} + +.meta-label { + font-size: 0.875rem; + color: var(--color-text-muted); +} + +.meta-value { + font-size: 0.875rem; + color: var(--color-text); +} + +.doc-tags .tag-pill { + cursor: pointer; +} + +.doc-tags .tag-pill:hover { + opacity: 0.8; +} + +/* === Document Editor === */ +.editor-container { + max-width: 900px; + margin: 0 auto; +} + +.editor-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-4) var(--space-6); + border-bottom: 1px solid var(--color-border); + background: var(--color-surface); +} + +.editor-header h2 { + font-size: 1rem; + font-weight: 600; +} + +.editor-form { + padding: var(--space-6); +} + +.form-row { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: var(--space-4); + margin-bottom: var(--space-4); +} + +.form-group { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.form-group label { + font-size: 0.875rem; + font-weight: 500; + color: var(--color-text-secondary); +} + +.form-group input, +.form-group select, +.form-group textarea { + padding: var(--space-3); + background: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + color: var(--color-text); +} + +.form-group input:focus, +.form-group select:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--color-accent); +} + +.form-group textarea { + min-height: 300px; + resize: vertical; + font-family: var(--font-mono); + line-height: 1.6; +} + +.form-group.full-width { + grid-column: 1 / -1; +} + +.editor-toolbar { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-3); + background: var(--color-surface); + border: 1px solid var(--color-border); + border-bottom: none; + border-radius: var(--radius-lg) var(--radius-lg) 0 0; +} + +.toolbar-btn { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-sm); + color: var(--color-text-secondary); + transition: var(--transition-fast); +} + +.toolbar-btn:hover { + background: var(--color-hover); + color: var(--color-text); +} + +.toolbar-separator { + width: 1px; + height: 20px; + background: var(--color-border); + margin: 0 var(--space-2); +} + +.toolbar-tabs { + margin-left: auto; + display: flex; + gap: var(--space-1); +} + +.tab-btn { + padding: var(--space-2) var(--space-3); + border-radius: var(--radius-sm); + font-size: 0.875rem; + color: var(--color-text-secondary); + transition: var(--transition-fast); +} + +.tab-btn:hover { + background: var(--color-hover); +} + +.tab-btn.active { + background: var(--color-accent-alpha); + color: var(--color-accent); +} + +.editor-content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0; + border: 1px solid var(--color-border); + border-radius: 0 0 var(--radius-lg) var(--radius-lg); + overflow: hidden; +} + +.editor-content.full { + grid-template-columns: 1fr; +} + +.editor-pane { + padding: var(--space-4); + background: var(--color-bg); +} + +.editor-pane:first-child { + border-right: 1px solid var(--color-border); +} + +.preview-pane { + background: var(--color-surface); + overflow-y: auto; +} + +.preview-toggle { + margin-left: auto; +} + +/* === Buttons === */ +.btn { + display: inline-flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-2) var(--space-4); + border-radius: var(--radius-md); + font-weight: 500; + font-size: 0.875rem; + transition: var(--transition-fast); +} + +.btn-primary { + background: var(--color-accent); + color: #000; +} + +.btn-primary:hover { + background: var(--color-accent-hover); +} + +.btn-ghost { + background: transparent; + color: var(--color-text-secondary); +} + +.btn-ghost:hover { + background: var(--color-hover); + color: var(--color-text); +} + +.btn-danger { + background: var(--color-danger); + color: white; +} + +.btn-danger:hover { + background: var(--color-danger-hover); +} + +.btn-icon-only { + width: 36px; + height: 36px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; +} + +/* === Modal === */ +.modal-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: var(--z-modal); + animation: fadeIn 0.15s ease; +} + +.modal { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-lg); + min-width: 400px; + max-width: 90vw; + animation: scaleIn 0.15s ease; +} + +.modal-header { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-4); + border-bottom: 1px solid var(--color-border); +} + +.modal-header h3 { + font-size: 1rem; + font-weight: 600; + flex: 1; +} + +.modal-body { + padding: var(--space-4); +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: var(--space-3); + padding: var(--space-4); + border-top: 1px solid var(--color-border); +} + +/* === Login Screen === */ +.login-screen { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: var(--color-bg); +} + +.login-card { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-xl); + padding: var(--space-8); + width: 100%; + max-width: 400px; + box-shadow: var(--shadow-lg); +} + +.login-card h1 { + font-size: 1.5rem; + font-weight: 700; + margin-bottom: var(--space-2); + color: var(--color-accent); + text-align: center; +} + +.login-card p { + color: var(--color-text-secondary); + text-align: center; + margin-bottom: var(--space-6); + font-size: 0.875rem; +} + +.login-form { + display: flex; + flex-direction: column; + gap: var(--space-4); +} + +.login-form .form-group { + gap: var(--space-2); +} + +.login-form input { + padding: var(--space-3); + background: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + color: var(--color-text); + font-family: var(--font-mono); + font-size: 0.875rem; +} + +.login-form input:focus { + outline: none; + border-color: var(--color-accent); +} + +.login-error { + color: var(--color-danger); + font-size: 0.875rem; + text-align: center; + display: none; +} + +.login-error.visible { + display: block; +} + +/* === Toast === */ +#toast-container { + position: fixed; + bottom: var(--space-6); + right: var(--space-6); + z-index: var(--z-toast); + display: flex; + flex-direction: column; + gap: var(--space-3); +} + +.toast { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-3) var(--space-4); + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + animation: slideInRight 0.2s ease; + min-width: 250px; +} + +.toast.success { + border-left: 4px solid var(--color-success); +} + +.toast.error { + border-left: 4px solid var(--color-danger); +} + +.toast.info { + border-left: 4px solid var(--color-info); +} + +.toast-message { + flex: 1; + font-size: 0.875rem; +} + +.toast-close { + color: var(--color-text-muted); + padding: var(--space-1); + border-radius: var(--radius-sm); +} + +.toast-close:hover { + background: var(--color-hover); + color: var(--color-text); +} + +/* === Empty State === */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: var(--space-16); + text-align: center; + color: var(--color-text-muted); +} + +.empty-state .icon { + font-size: 3rem; + margin-bottom: var(--space-4); + opacity: 0.5; +} + +.empty-state h3 { + font-size: 1.125rem; + font-weight: 600; + color: var(--color-text-secondary); + margin-bottom: var(--space-2); +} + +.empty-state p { + margin-bottom: var(--space-6); +} + +/* === Loading === */ +.loading { + display: flex; + align-items: center; + justify-content: center; + padding: var(--space-16); + color: var(--color-text-muted); +} + +.loading::before { + content: ''; + width: 24px; + height: 24px; + border: 2px solid var(--color-border); + border-top-color: var(--color-accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; + margin-right: var(--space-3); +} + +/* === Breadcrumbs === */ +.breadcrumbs { + display: flex; + align-items: center; + gap: var(--space-2); + font-size: 0.875rem; + color: var(--color-text-secondary); + margin-bottom: var(--space-4); +} + +.breadcrumb-item { + color: var(--color-text-secondary); + cursor: pointer; + transition: var(--transition-fast); +} + +.breadcrumb-item:hover { + color: var(--color-accent); +} + +.breadcrumb-separator { + color: var(--color-text-muted); +} + +/* === Quick Links === */ +.quick-links { + display: flex; + flex-direction: column; + gap: var(--space-1); + margin-top: var(--space-4); + padding-top: var(--space-4); + border-top: 1px solid var(--color-border); +} + +.quick-link { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-2) var(--space-3); + border-radius: var(--radius-md); + color: var(--color-text-secondary); + font-size: 0.875rem; + cursor: pointer; + transition: var(--transition-fast); +} + +.quick-link:hover { + background: var(--color-hover); + color: var(--color-text); +} + +/* === Type Badges === */ +.type-badge { + display: inline-flex; + align-items: center; + padding: 2px 8px; + border-radius: var(--radius-sm); + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; +} + +.type-badge.requirement { + background: var(--color-accent-alpha); + color: var(--color-requirement); +} + +.type-badge.note { + background: var(--color-info-bg); + color: var(--color-note); +} + +.type-badge.spec { + background: rgba(155, 89, 182, 0.15); + color: var(--color-spec); +} + +.type-badge.general { + background: var(--color-hover); + color: var(--color-text-muted); +} + +/* === Animations === */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes scaleIn { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } +} + +@keyframes slideInRight { + from { opacity: 0; transform: translateX(100%); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* === Responsive === */ +@media (max-width: 1024px) { + .sidebar { + display: none; + } + + .doc-sidebar { + display: none; + } + + .editor-content { + grid-template-columns: 1fr; + } + + .editor-pane:first-child { + border-right: none; + } + + .preview-pane { + display: none; + } + + .editor-content.show-preview .editor-pane:first-child { + display: none; + } + + .editor-content.show-preview .preview-pane { + display: block; + } +} + +@media (max-width: 768px) { + .app-header .search-box { + display: none; + } + + .content-body { + padding: var(--space-4); + } + + .doc-grid { + grid-template-columns: 1fr; + } + + .modal { + min-width: auto; + margin: var(--space-4); + } + + .login-card { + margin: var(--space-4); + padding: var(--space-6); + } +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..01feca5 --- /dev/null +++ b/public/index.html @@ -0,0 +1,18 @@ + + +
+ + +${message}
+${searchQuery || selectedTag ? 'Try adjusting your filters' : 'Create your first document'}
+ +$1')
+ .replace(/^- (.+)$/gm, '') + .replace(/^(.+)$/gm, (match) => { + if (match.startsWith('<')) return match; + return `
${match}
`; + }); +} diff --git a/public/js/views/editor.js b/public/js/views/editor.js new file mode 100644 index 0000000..d01d6db --- /dev/null +++ b/public/js/views/editor.js @@ -0,0 +1,259 @@ +// Editor View + +import { api } from '../api.js'; + +export async function renderEditor(app) { + const { id, libraryId } = app.state.params; + let doc = null; + let libraries = []; + + if (id) { + try { + doc = await api.getDocument(id); + } catch (e) { + app.showToast('Failed to load document', 'error'); + app.navigate('dashboard'); + return; + } + } + + try { + libraries = await api.getLibraries(); + } catch (e) {} + + const isNew = !id; + const appEl = document.getElementById('app'); + + let formData = { + title: doc?.title || '', + content: doc?.content || '', + tags: doc?.tags?.join(', ') || '', + type: doc?.type || 'general', + priority: doc?.priority || 'medium', + status: doc?.status || 'draft', + libraryId: doc?.libraryId || libraryId || '' + }; + + let showPreview = false; + let hasChanges = false; + + function render() { + appEl.innerHTML = ` +Nothing to preview
'; + + if (typeof marked !== 'undefined') { + return marked.parse(content); + } + + // Fallback + return content + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/^### (.+)$/gm, '$1')
+ .replace(/\n\n/g, ''); +} diff --git a/public/js/views/login.js b/public/js/views/login.js new file mode 100644 index 0000000..274283e --- /dev/null +++ b/public/js/views/login.js @@ -0,0 +1,29 @@ +// Login View + +export function renderLogin({ onLogin }) { + return ` +
Enter your API token to continue
+ +