From 1d1d7ea7fbad5a40bb22735e66ae78ffbaf5c78d Mon Sep 17 00:00:00 2001 From: Naman Rai Date: Sat, 7 Mar 2026 16:35:10 +0530 Subject: [PATCH 1/5] added info meged theme and general settings --- src/components/features/FloatingHub.tsx | 27 +- .../features/settings/AccountSettings.tsx | 129 -------- .../features/settings/GeneralSettings.tsx | 120 ------- src/components/features/settings/Info.tsx | 110 +++++++ src/components/features/settings/Settings.tsx | 181 +++++++++++ .../features/settings/ThemeSettings.tsx | 304 ------------------ src/components/layout/editor/Editor.tsx | 16 +- src/components/layout/editor/EditorTabs.tsx | 12 +- src/theme/cinderTheme.ts | 24 +- src/theme/markdown.css | 43 ++- 10 files changed, 341 insertions(+), 625 deletions(-) delete mode 100644 src/components/features/settings/AccountSettings.tsx delete mode 100644 src/components/features/settings/GeneralSettings.tsx create mode 100644 src/components/features/settings/Info.tsx create mode 100644 src/components/features/settings/Settings.tsx delete mode 100644 src/components/features/settings/ThemeSettings.tsx diff --git a/src/components/features/FloatingHub.tsx b/src/components/features/FloatingHub.tsx index a3277f2..f49dffb 100644 --- a/src/components/features/FloatingHub.tsx +++ b/src/components/features/FloatingHub.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useRef } from 'react'; import { createPortal } from 'react-dom'; -import { User, Palette, Settings } from 'lucide-react'; +import { Info, Settings } from 'lucide-react'; import { useAppStore } from '../../store/useAppStore'; export function FloatingHub() { @@ -86,41 +86,28 @@ export function FloatingHub() {
-
diff --git a/src/components/features/settings/AccountSettings.tsx b/src/components/features/settings/AccountSettings.tsx deleted file mode 100644 index 70bc31e..0000000 --- a/src/components/features/settings/AccountSettings.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { useRef } from 'react'; -import { User, Shield, KeyRound, Globe, Save } from 'lucide-react'; - -export function AccountSettings() { - const nameRef = useRef(null); - const emailRef = useRef(null); - - return ( -
-
- {/* Header */} -
-
-

- Account Settings -

-

- Manage your profile and security preferences -

-
- -
- - {/* Profile Section */} -
-
-
- 👤 -
-
- -

- JPG, GIF or PNG. Max size of 800K -

-
-
- -
-
- -
- - -
-
-
- -
- - -
-
-
-
- - {/* Integration List / Security (Placeholder) */} -
-

- Security -

- -
- {[ - { - icon: KeyRound, - title: 'Password', - desc: 'Last changed 3 months ago', - action: 'Update', - }, - { - icon: Shield, - title: 'Two-Factor Authentication', - desc: 'Currently disabled', - action: 'Enable', - }, - ].map((item, idx) => ( -
-
-
- -
-
-

- {item.title} -

-

- {item.desc} -

-
-
- -
- ))} -
-
-
-
- ); -} diff --git a/src/components/features/settings/GeneralSettings.tsx b/src/components/features/settings/GeneralSettings.tsx deleted file mode 100644 index 605f1ce..0000000 --- a/src/components/features/settings/GeneralSettings.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { Monitor, Bell, FolderOpen, Globe } from 'lucide-react'; - -export function GeneralSettings() { - return ( -
-
- {/* Header */} -
-
-

- General Settings -

-

- Customize your editor experience -

-
-
- - {/* Content */} -
-

- Appearance -

-
- {[ - { - icon: Monitor, - title: 'Default View', - desc: 'Choose default view for new files', - action: 'Split View', - options: ['Split View', 'Editor Only', 'Preview Only'], - }, - { - icon: FolderOpen, - title: 'Sidebar Position', - desc: 'Change the location of the sidebar', - action: 'Left', - options: ['Left', 'Right'], - }, - ].map((item, idx) => ( -
-
-
- -
-
-

- {item.title} -

-

- {item.desc} -

-
-
- -
- ))} -
- -

- System -

-
- {[ - { - icon: Globe, - title: 'Language', - desc: 'Change interface language', - action: 'English', - options: ['English', 'Spanish', 'French', 'German', 'Japanese'], - }, - { - icon: Bell, - title: 'Notifications', - desc: 'Configure desktop notifications', - action: 'All', - options: ['All', 'Important Only', 'None'], - }, - ].map((item, idx) => ( -
-
-
- -
-
-

- {item.title} -

-

- {item.desc} -

-
-
- -
- ))} -
-
-
-
- ); -} diff --git a/src/components/features/settings/Info.tsx b/src/components/features/settings/Info.tsx new file mode 100644 index 0000000..0f96d65 --- /dev/null +++ b/src/components/features/settings/Info.tsx @@ -0,0 +1,110 @@ +import { Github, ExternalLink, Mail, Info as InfoIcon } from 'lucide-react'; + +export function Info() { + return ( +
+
+ {/* Header */} +
+
+
+ +
+
+

+ About Cinder Notes +

+

+ The minimalist, distraction-free markdown powerhouse. +

+
+
+
+ + {/* Content Section */} +
+
+
+

What is Cinder?

+

+ Cinder Notes is a high-performance markdown editor designed for speed and focus. + Built with a "less is more" philosophy, it provides a clean canvas for your thoughts + while keeping powerful tools just a click away in the Floating Hub. +

+
+ +
+

Core Philosophy

+
    +
  • +
    + Personalized through themes, not bloat. +
  • +
  • +
    + Privacy first: Your notes stay on your machine. +
  • +
  • +
    + Native performance with lightweight components. +
  • +
+
+
+ +
+ + +
+

Connect

+

+ Have feedback or want to request a feature? We'd love to hear from you. +

+ +
+
+
+ + {/* Footer */} +
+

+ Cinder Notes v1.0.0 • Built with focus by Aurelius Labs +

+
+
+
+ ); +} diff --git a/src/components/features/settings/Settings.tsx b/src/components/features/settings/Settings.tsx new file mode 100644 index 0000000..e8c8b6e --- /dev/null +++ b/src/components/features/settings/Settings.tsx @@ -0,0 +1,181 @@ +import { useState, useEffect } from 'react'; +import { + Sun, + Moon, + Monitor, + Lock, + Settings as SettingsIcon, + Palette, + Bell, + FolderOpen, + Globe +} from 'lucide-react'; + +interface ThemePreset { + id: string; + name: string; + value: string; + gradient: string; + accent: string; + disabled?: boolean; +} + +const THEME_PRESETS: ThemePreset[] = [ + { id: 'cinder-dark', name: 'Cinder Dark', value: '', gradient: 'linear-gradient(135deg, #1f1f23 0%, #141417 100%)', accent: '#f48c25' }, + { id: 'cinder-light', name: 'Cinder Light', value: 'theme-cinder-light', gradient: 'linear-gradient(135deg, #fdfaf0 0%, #f2efe7 100%)', accent: '#d97706' }, + { id: 'zen-black', name: 'Zen Black', value: 'theme-zen-black', gradient: 'linear-gradient(135deg, #0a0a0a 0%, #000000 100%)', accent: '#333' }, + { id: 'synthwave', name: "Synthwave '84", value: 'theme-synthwave', gradient: 'linear-gradient(135deg, #2a2139 0%, #262335 100%)', accent: '#ff7edb' }, + { id: 'github-dark', name: 'GitHub Dark', value: 'theme-github-dark', gradient: 'linear-gradient(135deg, #161b22 0%, #0d1117 100%)', accent: '#58a6ff' }, + { id: 'monokai', name: 'Monokai Pro', value: 'theme-monokai', gradient: 'linear-gradient(135deg, #272822 0%, #1e1f1c 100%)', accent: '#a6e22e' }, + { id: 'dracula', name: 'Dracula', value: 'theme-dracula', gradient: 'linear-gradient(135deg, #282a36 0%, #21222c 100%)', accent: '#bd93f9' }, + { id: 'nord', name: 'Nord', value: 'theme-nord', gradient: 'linear-gradient(135deg, #3b4252 0%, #2e3440 100%)', accent: '#88c0d0' }, + { id: 'forest', name: 'Forest', value: 'theme-forest', gradient: 'linear-gradient(135deg, #273329 0%, #1e2820 100%)', accent: '#a7c957' }, + { id: 'mustard', name: 'Muddy Mustard', value: 'theme-mustard', gradient: 'linear-gradient(135deg, #3b3728 0%, #2d2a1e 100%)', accent: '#e9c46a' }, + { id: 'marine', name: 'Marine', value: 'theme-marine', gradient: 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)', accent: '#38bdf8' }, + { id: 'ember', name: 'Ember', value: 'theme-ember', gradient: 'linear-gradient(135deg, #3f1818 0%, #2b1111 100%)', accent: '#f87171' }, +]; + +const THEME_VARIABLES = [ + '--bg-primary', '--bg-secondary', '--bg-tertiary', '--bg-hover', '--bg-active', + '--text-primary', '--text-secondary', '--text-tertiary', '--editor-header-accent', + '--accent-glow', '--border-primary', '--border-secondary', '--markdown-heading', + '--markdown-link', '--markdown-code', '--markdown-code-bg', '--editor-bg', + '--editor-text', '--editor-selection-bg', '--activity-bar-bg', +]; + +export function Settings() { + const [activeTab, setActiveTab] = useState<'general' | 'theme'>('general'); + const [currentTheme, setCurrentTheme] = useState(() => localStorage.getItem('cinder-theme') || ''); + const [colorMode, setColorMode] = useState<'light' | 'dark' | 'system'>('dark'); + + useEffect(() => { + THEME_PRESETS.forEach((t) => { + if (t.value) document.documentElement.classList.remove(t.value); + }); + + THEME_VARIABLES.forEach((v) => document.documentElement.style.removeProperty(v)); + + if (currentTheme) { + document.documentElement.classList.add(currentTheme); + } + localStorage.setItem('cinder-theme', currentTheme); + }, [currentTheme]); + + return ( +
+ {/* Sidebar-style Tabs */} +
+
+

Settings

+ + +
+ + {/* Content Area */} +
+
+ {activeTab === 'general' ? ( +
+
+

General Settings

+

Customize your editor experience

+
+ +
+

Editor

+
+ {[ + { icon: Monitor, title: 'Default View', desc: 'Choose default view for new files', options: ['Split View', 'Editor Only', 'Preview Only'] }, + { icon: FolderOpen, title: 'Sidebar Position', desc: 'Change the location of the sidebar', options: ['Left', 'Right'] }, + ].map((item, idx) => ( +
+
+
+
+

{item.title}

+

{item.desc}

+
+
+ +
+ ))} +
+ +

System

+
+ {[ + { icon: Globe, title: 'Language', desc: 'Change interface language', options: ['English', 'Spanish', 'French', 'German', 'Japanese'] }, + { icon: Bell, title: 'Notifications', desc: 'Configure desktop notifications', options: ['All', 'Important Only', 'None'] }, + ].map((item, idx) => ( +
+
+
+
+

{item.title}

+

{item.desc}

+
+
+ +
+ ))} +
+
+
+ ) : ( +
+
+

Themes

+

Manage app appearance and customization

+
+ +
+

Color Mode

+
+ {[{ id: 'light', label: 'Light mode', icon: Sun }, { id: 'dark', label: 'Dark mode', icon: Moon }, { id: 'system', label: 'System', icon: Monitor }].map((mode) => ( + + ))} +
+
+ +
+

Preset themes

+
+ {THEME_PRESETS.map((theme) => { + const isActive = currentTheme === theme.value && !theme.disabled; + return ( + + ); + })} +
+
+
+ )} +
+
+
+
+ ); +} diff --git a/src/components/features/settings/ThemeSettings.tsx b/src/components/features/settings/ThemeSettings.tsx deleted file mode 100644 index e401306..0000000 --- a/src/components/features/settings/ThemeSettings.tsx +++ /dev/null @@ -1,304 +0,0 @@ -import { useState, useEffect } from 'react'; -import { Upload, Shuffle, Sun, Moon, Monitor, Lock } from 'lucide-react'; - -interface ThemePreset { - id: string; - name: string; - value: string; - gradient: string; - accent: string; - disabled?: boolean; -} - -const THEME_PRESETS: ThemePreset[] = [ - { - id: 'cinder-dark', - name: 'Cinder Dark', - value: '', - gradient: 'linear-gradient(135deg, #1f1f23 0%, #141417 100%)', - accent: '#f48c25', - }, - { - id: 'cinder-light', - name: 'Cinder Light', - value: 'theme-cinder-light', - gradient: 'linear-gradient(135deg, #fdfaf0 0%, #f2efe7 100%)', - accent: '#d97706', - }, - { - id: 'zen-black', - name: 'Zen Black', - value: 'theme-zen-black', - gradient: 'linear-gradient(135deg, #0a0a0a 0%, #000000 100%)', - accent: '#333', - }, - // New Functional Themes - { - id: 'synthwave', - name: "Synthwave '84", - value: 'theme-synthwave', - gradient: 'linear-gradient(135deg, #2a2139 0%, #262335 100%)', - accent: '#ff7edb', - }, - { - id: 'github-dark', - name: 'GitHub Dark', - value: 'theme-github-dark', - gradient: 'linear-gradient(135deg, #161b22 0%, #0d1117 100%)', - accent: '#58a6ff', - }, - { - id: 'monokai', - name: 'Monokai Pro', - value: 'theme-monokai', - gradient: 'linear-gradient(135deg, #272822 0%, #1e1f1c 100%)', - accent: '#a6e22e', - }, - { - id: 'dracula', - name: 'Dracula', - value: 'theme-dracula', - gradient: 'linear-gradient(135deg, #282a36 0%, #21222c 100%)', - accent: '#bd93f9', - }, - { - id: 'nord', - name: 'Nord', - value: 'theme-nord', - gradient: 'linear-gradient(135deg, #3b4252 0%, #2e3440 100%)', - accent: '#88c0d0', - }, - { - id: 'forest', - name: 'Forest', - value: 'theme-forest', - gradient: 'linear-gradient(135deg, #273329 0%, #1e2820 100%)', - accent: '#a7c957', - }, - { - id: 'mustard', - name: 'Muddy Mustard', - value: 'theme-mustard', - gradient: 'linear-gradient(135deg, #3b3728 0%, #2d2a1e 100%)', - accent: '#e9c46a', - }, - { - id: 'marine', - name: 'Marine', - value: 'theme-marine', - gradient: 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)', - accent: '#38bdf8', - }, - { - id: 'ember', - name: 'Ember', - value: 'theme-ember', - gradient: 'linear-gradient(135deg, #3f1818 0%, #2b1111 100%)', - accent: '#f87171', - }, -]; - -export function ThemeSettings() { - const [currentTheme, setCurrentTheme] = useState( - () => localStorage.getItem('cinder-theme') || '' - ); - const [colorMode, setColorMode] = useState<'light' | 'dark' | 'system'>( - 'dark' - ); // Simplified for now - - // Apply theme changes - useEffect(() => { - // Reset classes - THEME_PRESETS.forEach((t) => { - if (t.value) document.documentElement.classList.remove(t.value); - }); - - // Add new theme class - if (currentTheme) { - document.documentElement.classList.add(currentTheme); - } - localStorage.setItem('cinder-theme', currentTheme); - }, [currentTheme]); - - return ( -
-
- {/* Header */} -
-
-

- Themes -

-

- Manage app appearance and customization -

-
-
- - -
-
- - {/* Color Mode Section */} -
-

- Color Mode -

-

- Choose if app's appearance should be light or dark, or follow your - computer's settings. -

- -
- {[ - { id: 'light', label: 'Light mode', icon: Sun }, - { id: 'dark', label: 'Dark mode', icon: Moon }, - { id: 'system', label: 'System', icon: Monitor }, - ].map((mode) => ( - - ))} -
-
- - {/* Preset Themes Grid */} -
-

- Preset themes -

-

- Choose a preset theme from our library. -

- -
- {THEME_PRESETS.map((theme) => { - const isActive = currentTheme === theme.value && !theme.disabled; - return ( - - ); - })} -
-
- - {/* Custom Themes (Placeholder UI) */} -
-
-
-

- Custom themes -

-

- Set your own app theme by changing the color for each interface - element. -

-
- {/* Toggle switch placeholder */} -
-
-
-
- -
- {[ - { label: 'Window background', hex: '#F1F5F9' }, - { label: 'Selected items', hex: '#F472B6' }, - { label: 'Online indication', hex: '#84CC16' }, - { label: 'Notifications', hex: '#6366F1' }, - { label: 'New inbox', hex: '#F97316' }, - { label: 'Sidebar', hex: '#7C3AED' }, - ].map((item, idx) => ( -
-
-
- {item.label} -
-
- {item.hex} -
-
-
-
- -
-
- ))} -
-
-
-
- ); -} diff --git a/src/components/layout/editor/Editor.tsx b/src/components/layout/editor/Editor.tsx index 3c9c986..1102788 100644 --- a/src/components/layout/editor/Editor.tsx +++ b/src/components/layout/editor/Editor.tsx @@ -2,9 +2,9 @@ import type { MutableRefObject } from 'react'; import { useAppStore } from '../../../store/useAppStore'; import { MarkdownPreview } from './MarkdownPreview'; import { Eye, ChevronLeft, FileText, Save } from 'lucide-react'; -import { AccountSettings } from '../../features/settings/AccountSettings'; -import { ThemeSettings } from '../../features/settings/ThemeSettings'; -import { GeneralSettings } from '../../features/settings/GeneralSettings'; + +import { Settings } from '../../features/settings/Settings'; +import { Info } from '../../features/settings/Info'; import { CodeMirrorEditor } from './CodeMirrorEditor'; import type { EditorView } from '@codemirror/view'; @@ -28,13 +28,13 @@ export function Editor({ style={{ backgroundColor: 'var(--editor-bg)' }} > {!activeFileId || - activeFileId === 'welcome' || - activeFileId.startsWith('cinder-') ? ( + activeFileId === 'welcome' || + activeFileId.startsWith('cinder-') ? ( /* --- SYSTEM TABS & EMPTY STATE --- */
- {activeFileId === 'cinder-account' && } - {activeFileId === 'cinder-theme' && } - {activeFileId === 'cinder-settings' && } + + {activeFileId === 'cinder-settings' && } + {activeFileId === 'cinder-info' && } {(!activeFileId || activeFileId === 'welcome') && (
diff --git a/src/components/layout/editor/EditorTabs.tsx b/src/components/layout/editor/EditorTabs.tsx index 4182e65..8bbdef7 100644 --- a/src/components/layout/editor/EditorTabs.tsx +++ b/src/components/layout/editor/EditorTabs.tsx @@ -6,8 +6,8 @@ import { Gift, Maximize2, Minimize2, - User, - Palette, + + Info, Settings, } from 'lucide-react'; import { useAppStore } from '../../../store/useAppStore'; @@ -55,9 +55,9 @@ export function EditorTabs() { let tabName = ''; if (isWelcomeTab) tabName = 'Welcome'; else if (isBlankTab) tabName = 'Untitled'; - else if (fileId === 'cinder-account') tabName = 'Account'; - else if (fileId === 'cinder-theme') tabName = 'Themes'; + else if (fileId === 'cinder-settings') tabName = 'Settings'; + else if (fileId === 'cinder-info') tabName = 'About'; else tabName = file?.name.replace(/\.md$/, '') || 'Unknown'; return ( @@ -118,9 +118,9 @@ export function EditorTabs() { color: isActive ? 'var(--editor-header-accent)' : 'inherit', }} > - {fileId === 'cinder-account' && } - {fileId === 'cinder-theme' && } + {fileId === 'cinder-settings' && } + {fileId === 'cinder-info' && } )} diff --git a/src/theme/cinderTheme.ts b/src/theme/cinderTheme.ts index 6d10615..459b4bd 100644 --- a/src/theme/cinderTheme.ts +++ b/src/theme/cinderTheme.ts @@ -38,11 +38,11 @@ const cinderEditorTheme = EditorView.theme( borderLeftWidth: '2px', }, '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': - { - backgroundColor: 'var(--editor-selection-bg) !important', - }, + { + backgroundColor: 'var(--editor-selection-bg) !important', + }, '.cm-activeLine': { - backgroundColor: 'rgba(255, 255, 255, 0.03)', + backgroundColor: 'var(--bg-active)', }, '.cm-gutters': { backgroundColor: 'var(--editor-bg)', @@ -76,21 +76,20 @@ const cinderEditorTheme = EditorView.theme( opacity: '0.75', }, '.cm-md-inline-code': { - backgroundColor: 'rgba(255, 255, 255, 0.06)', + backgroundColor: 'var(--bg-tertiary)', padding: '0.25em 0.5em', borderRadius: '6px', fontSize: '0.85em', fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', - border: '1px solid rgba(255, 255, 255, 0.08)', + border: '1px solid var(--border-secondary)', color: 'var(--text-primary)', - boxShadow: '0 1px 2px rgba(0, 0, 0, 0.1)', }, '.cm-codeblock-line': { - backgroundColor: 'rgba(0, 0, 0, 0.2)', + backgroundColor: 'var(--bg-secondary)', paddingLeft: '1.3em', paddingRight: '1.3em', - borderLeft: '1px solid rgba(255, 255, 255, 0.08)', - borderRight: '1px solid rgba(255, 255, 255, 0.08)', + borderLeft: '1px solid var(--border-secondary)', + borderRight: '1px solid var(--border-secondary)', }, '.cm-codeblock-line *': { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', @@ -118,15 +117,14 @@ const cinderEditorTheme = EditorView.theme( fontSize: '1.1rem !important', }, '.cm-blockquote-line': { - background: - 'linear-gradient(to right, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.01))', + backgroundColor: 'var(--bg-active)', boxShadow: 'inset 4px 0 0 var(--editor-header-accent)', color: 'var(--text-secondary)', paddingLeft: '1.6em', paddingTop: '0.2em', paddingBottom: '0.2em', fontStyle: 'italic', - border: '1px solid rgba(255, 255, 255, 0.03)', + border: '1px solid var(--border-secondary)', borderLeft: 'none', }, diff --git a/src/theme/markdown.css b/src/theme/markdown.css index 0edce41..ebf0a06 100644 --- a/src/theme/markdown.css +++ b/src/theme/markdown.css @@ -15,7 +15,7 @@ } /* Vertical rhythm */ -.markdown-preview > * + * { +.markdown-preview>*+* { margin-top: 1.2em; } @@ -109,8 +109,8 @@ white-space: pre-wrap; } -.markdown-preview ul > li::marker, -.markdown-preview ol > li::marker { +.markdown-preview ul>li::marker, +.markdown-preview ol>li::marker { color: var(--markdown-list); } @@ -126,14 +126,13 @@ INLINE CODE ===================================================== */ .markdown-preview code:not(pre code) { - background: rgba(255, 255, 255, 0.06); + background: var(--bg-tertiary); padding: 0.25em 0.5em; border-radius: 6px; font-size: 0.85em; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - border: 1px solid rgba(255, 255, 255, 0.08); + border: 1px solid var(--border-secondary); color: var(--text-primary); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } /* ===================================================== @@ -143,11 +142,9 @@ margin: 2em 0; border-radius: 12px; overflow: hidden; - background: rgba(0, 0, 0, 0.2); - border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: - 0 10px 40px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.05); + background: var(--bg-secondary); + border: 1px solid var(--border-secondary); + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1); } .markdown-preview .code-block-header { @@ -155,8 +152,8 @@ align-items: center; justify-content: space-between; padding: 0.6em 1em; - background: rgba(255, 255, 255, 0.02); - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-tertiary); + border-bottom: 1px solid var(--border-secondary); font-size: 0.72em; letter-spacing: 0.1em; text-transform: uppercase; @@ -192,18 +189,14 @@ BLOCKQUOTE ===================================================== */ .markdown-preview blockquote { - background: linear-gradient( - to right, - rgba(255, 255, 255, 0.05), - rgba(255, 255, 255, 0.01) - ); + background: var(--bg-active); padding: 1.2em 1.6em; border-radius: 4px 12px 12px 4px; margin: 2em 0; box-shadow: inset 4px 0 0 var(--editor-header-accent); color: var(--text-secondary); font-style: italic; - border: 1px solid rgba(255, 255, 255, 0.03); + border: 1px solid var(--border-secondary); border-left: none; } @@ -226,16 +219,16 @@ .markdown-preview th, .markdown-preview td { padding: 0.75em 1em; - border: 1px solid rgba(255, 255, 255, 0.06); + border: 1px solid var(--border-secondary); } .markdown-preview th { - background: rgba(255, 255, 255, 0.04); + background: var(--bg-tertiary); font-weight: 600; } .markdown-preview tr:nth-child(even) { - background: rgba(255, 255, 255, 0.015); + background: var(--bg-active); } /* ===================================================== @@ -274,7 +267,7 @@ padding: 0.5em 0; } -.markdown-preview .katex-display > .katex { +.markdown-preview .katex-display>.katex { font-size: 1.08em; } @@ -282,7 +275,7 @@ SELECTION ===================================================== */ .markdown-preview ::selection { - background: rgba(100, 150, 255, 0.25); + background: var(--editor-selection-bg); } /* ===================================================== @@ -300,4 +293,4 @@ .markdown-preview::-webkit-scrollbar-thumb:hover { background: var(--border-primary); -} +} \ No newline at end of file From 90eaf9fe3c4888b1cdf58cde53ade2b789526532 Mon Sep 17 00:00:00 2001 From: Naman Rai Date: Sun, 8 Mar 2026 00:22:52 +0530 Subject: [PATCH 2/5] fixed for prettier --- package.json | 3 +- src/components/features/settings/Info.tsx | 225 ++++---- src/components/features/settings/Settings.tsx | 519 ++++++++++++------ src/components/layout/editor/Editor.tsx | 5 +- src/components/layout/editor/EditorTabs.tsx | 3 - src/theme/cinderTheme.ts | 6 +- src/theme/markdown.css | 10 +- 7 files changed, 504 insertions(+), 267 deletions(-) diff --git a/package.json b/package.json index 5b51c3e..a92130e 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "tauri:dev": "npx tauri dev", "tauri:build": "npx tauri build", "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"", + "format:write": "prettier --write \"src/**/*.{ts,tsx,css}\"", "prepare": "husky" }, "dependencies": { @@ -58,4 +59,4 @@ "typescript-eslint": "^8.46.4", "vite": "^7.2.4" } -} +} \ No newline at end of file diff --git a/src/components/features/settings/Info.tsx b/src/components/features/settings/Info.tsx index 0f96d65..9731891 100644 --- a/src/components/features/settings/Info.tsx +++ b/src/components/features/settings/Info.tsx @@ -1,110 +1,139 @@ import { Github, ExternalLink, Mail, Info as InfoIcon } from 'lucide-react'; export function Info() { - return ( -
-
- {/* Header */} -
-
-
- -
-
-

- About Cinder Notes -

-

- The minimalist, distraction-free markdown powerhouse. -

-
-
-
- - {/* Content Section */} -
-
-
-

What is Cinder?

-

- Cinder Notes is a high-performance markdown editor designed for speed and focus. - Built with a "less is more" philosophy, it provides a clean canvas for your thoughts - while keeping powerful tools just a click away in the Floating Hub. -

-
+ return ( +
+
+ {/* Header */} +
+
+
+ +
+
+

+ About Cinder Notes +

+

+ The minimalist, distraction-free markdown powerhouse. +

+
+
+
-
-

Core Philosophy

-
    -
  • -
    - Personalized through themes, not bloat. -
  • -
  • -
    - Privacy first: Your notes stay on your machine. -
  • -
  • -
    - Native performance with lightweight components. -
  • -
-
-
+ {/* Content Section */} +
+
+
+

+ What is Cinder? +

+

+ Cinder Notes is a high-performance markdown editor designed for + speed and focus. Built with a "less is more" philosophy, it + provides a clean canvas for your thoughts while keeping powerful + tools just a click away in the Floating Hub. +

+
-
-
-

Resources

+
+

+ Core Philosophy +

+
    +
  • +
    + Personalized through themes, not bloat. +
  • +
  • +
    + Privacy first: Your notes stay on your machine. +
  • +
  • +
    + Native performance with lightweight components. +
  • +
+
+
-
- -
- - GitHub Repository -
- -
+
+
+

+ Resources +

- -
- - Documentation -
- -
-
-
+
+ +
+ + + GitHub Repository + +
+ +
-
-

Connect

-

- Have feedback or want to request a feature? We'd love to hear from you. -

- -
-
-
+ +
+ + + Documentation + +
+ +
+
+
- {/* Footer */} -
-

- Cinder Notes v1.0.0 • Built with focus by Aurelius Labs -

-
+
+

+ Connect +

+

+ Have feedback or want to request a feature? We'd love to hear + from you. +

+
+
+
+ + {/* Footer */} +
+

+ Cinder Notes v1.0.0 • Built with focus by Aurelius Labs +

- ); +
+
+ ); } diff --git a/src/components/features/settings/Settings.tsx b/src/components/features/settings/Settings.tsx index e8c8b6e..e98283f 100644 --- a/src/components/features/settings/Settings.tsx +++ b/src/components/features/settings/Settings.tsx @@ -1,181 +1,392 @@ import { useState, useEffect } from 'react'; import { - Sun, - Moon, - Monitor, - Lock, - Settings as SettingsIcon, - Palette, - Bell, - FolderOpen, - Globe + Sun, + Moon, + Monitor, + Lock, + Settings as SettingsIcon, + Palette, + Bell, + FolderOpen, + Globe, } from 'lucide-react'; interface ThemePreset { - id: string; - name: string; - value: string; - gradient: string; - accent: string; - disabled?: boolean; + id: string; + name: string; + value: string; + gradient: string; + accent: string; + disabled?: boolean; } const THEME_PRESETS: ThemePreset[] = [ - { id: 'cinder-dark', name: 'Cinder Dark', value: '', gradient: 'linear-gradient(135deg, #1f1f23 0%, #141417 100%)', accent: '#f48c25' }, - { id: 'cinder-light', name: 'Cinder Light', value: 'theme-cinder-light', gradient: 'linear-gradient(135deg, #fdfaf0 0%, #f2efe7 100%)', accent: '#d97706' }, - { id: 'zen-black', name: 'Zen Black', value: 'theme-zen-black', gradient: 'linear-gradient(135deg, #0a0a0a 0%, #000000 100%)', accent: '#333' }, - { id: 'synthwave', name: "Synthwave '84", value: 'theme-synthwave', gradient: 'linear-gradient(135deg, #2a2139 0%, #262335 100%)', accent: '#ff7edb' }, - { id: 'github-dark', name: 'GitHub Dark', value: 'theme-github-dark', gradient: 'linear-gradient(135deg, #161b22 0%, #0d1117 100%)', accent: '#58a6ff' }, - { id: 'monokai', name: 'Monokai Pro', value: 'theme-monokai', gradient: 'linear-gradient(135deg, #272822 0%, #1e1f1c 100%)', accent: '#a6e22e' }, - { id: 'dracula', name: 'Dracula', value: 'theme-dracula', gradient: 'linear-gradient(135deg, #282a36 0%, #21222c 100%)', accent: '#bd93f9' }, - { id: 'nord', name: 'Nord', value: 'theme-nord', gradient: 'linear-gradient(135deg, #3b4252 0%, #2e3440 100%)', accent: '#88c0d0' }, - { id: 'forest', name: 'Forest', value: 'theme-forest', gradient: 'linear-gradient(135deg, #273329 0%, #1e2820 100%)', accent: '#a7c957' }, - { id: 'mustard', name: 'Muddy Mustard', value: 'theme-mustard', gradient: 'linear-gradient(135deg, #3b3728 0%, #2d2a1e 100%)', accent: '#e9c46a' }, - { id: 'marine', name: 'Marine', value: 'theme-marine', gradient: 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)', accent: '#38bdf8' }, - { id: 'ember', name: 'Ember', value: 'theme-ember', gradient: 'linear-gradient(135deg, #3f1818 0%, #2b1111 100%)', accent: '#f87171' }, + { + id: 'cinder-dark', + name: 'Cinder Dark', + value: '', + gradient: 'linear-gradient(135deg, #1f1f23 0%, #141417 100%)', + accent: '#f48c25', + }, + { + id: 'cinder-light', + name: 'Cinder Light', + value: 'theme-cinder-light', + gradient: 'linear-gradient(135deg, #fdfaf0 0%, #f2efe7 100%)', + accent: '#d97706', + }, + { + id: 'zen-black', + name: 'Zen Black', + value: 'theme-zen-black', + gradient: 'linear-gradient(135deg, #0a0a0a 0%, #000000 100%)', + accent: '#333', + }, + { + id: 'synthwave', + name: "Synthwave '84", + value: 'theme-synthwave', + gradient: 'linear-gradient(135deg, #2a2139 0%, #262335 100%)', + accent: '#ff7edb', + }, + { + id: 'github-dark', + name: 'GitHub Dark', + value: 'theme-github-dark', + gradient: 'linear-gradient(135deg, #161b22 0%, #0d1117 100%)', + accent: '#58a6ff', + }, + { + id: 'monokai', + name: 'Monokai Pro', + value: 'theme-monokai', + gradient: 'linear-gradient(135deg, #272822 0%, #1e1f1c 100%)', + accent: '#a6e22e', + }, + { + id: 'dracula', + name: 'Dracula', + value: 'theme-dracula', + gradient: 'linear-gradient(135deg, #282a36 0%, #21222c 100%)', + accent: '#bd93f9', + }, + { + id: 'nord', + name: 'Nord', + value: 'theme-nord', + gradient: 'linear-gradient(135deg, #3b4252 0%, #2e3440 100%)', + accent: '#88c0d0', + }, + { + id: 'forest', + name: 'Forest', + value: 'theme-forest', + gradient: 'linear-gradient(135deg, #273329 0%, #1e2820 100%)', + accent: '#a7c957', + }, + { + id: 'mustard', + name: 'Muddy Mustard', + value: 'theme-mustard', + gradient: 'linear-gradient(135deg, #3b3728 0%, #2d2a1e 100%)', + accent: '#e9c46a', + }, + { + id: 'marine', + name: 'Marine', + value: 'theme-marine', + gradient: 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)', + accent: '#38bdf8', + }, + { + id: 'ember', + name: 'Ember', + value: 'theme-ember', + gradient: 'linear-gradient(135deg, #3f1818 0%, #2b1111 100%)', + accent: '#f87171', + }, ]; const THEME_VARIABLES = [ - '--bg-primary', '--bg-secondary', '--bg-tertiary', '--bg-hover', '--bg-active', - '--text-primary', '--text-secondary', '--text-tertiary', '--editor-header-accent', - '--accent-glow', '--border-primary', '--border-secondary', '--markdown-heading', - '--markdown-link', '--markdown-code', '--markdown-code-bg', '--editor-bg', - '--editor-text', '--editor-selection-bg', '--activity-bar-bg', + '--bg-primary', + '--bg-secondary', + '--bg-tertiary', + '--bg-hover', + '--bg-active', + '--text-primary', + '--text-secondary', + '--text-tertiary', + '--editor-header-accent', + '--accent-glow', + '--border-primary', + '--border-secondary', + '--markdown-heading', + '--markdown-link', + '--markdown-code', + '--markdown-code-bg', + '--editor-bg', + '--editor-text', + '--editor-selection-bg', + '--activity-bar-bg', ]; export function Settings() { - const [activeTab, setActiveTab] = useState<'general' | 'theme'>('general'); - const [currentTheme, setCurrentTheme] = useState(() => localStorage.getItem('cinder-theme') || ''); - const [colorMode, setColorMode] = useState<'light' | 'dark' | 'system'>('dark'); + const [activeTab, setActiveTab] = useState<'general' | 'theme'>('general'); + const [currentTheme, setCurrentTheme] = useState( + () => localStorage.getItem('cinder-theme') || '' + ); + const [colorMode, setColorMode] = useState<'light' | 'dark' | 'system'>( + 'dark' + ); - useEffect(() => { - THEME_PRESETS.forEach((t) => { - if (t.value) document.documentElement.classList.remove(t.value); - }); + useEffect(() => { + THEME_PRESETS.forEach((t) => { + if (t.value) document.documentElement.classList.remove(t.value); + }); - THEME_VARIABLES.forEach((v) => document.documentElement.style.removeProperty(v)); + THEME_VARIABLES.forEach((v) => + document.documentElement.style.removeProperty(v) + ); - if (currentTheme) { - document.documentElement.classList.add(currentTheme); - } - localStorage.setItem('cinder-theme', currentTheme); - }, [currentTheme]); + if (currentTheme) { + document.documentElement.classList.add(currentTheme); + } + localStorage.setItem('cinder-theme', currentTheme); + }, [currentTheme]); - return ( -
- {/* Sidebar-style Tabs */} -
-
-

Settings

- - -
+ return ( +
+ {/* Sidebar-style Tabs */} +
+
+

+ Settings +

+ + +
- {/* Content Area */} -
-
- {activeTab === 'general' ? ( -
-
-

General Settings

-

Customize your editor experience

-
+ {/* Content Area */} +
+
+ {activeTab === 'general' ? ( +
+
+

+ General Settings +

+

+ Customize your editor experience +

+
-
-

Editor

-
- {[ - { icon: Monitor, title: 'Default View', desc: 'Choose default view for new files', options: ['Split View', 'Editor Only', 'Preview Only'] }, - { icon: FolderOpen, title: 'Sidebar Position', desc: 'Change the location of the sidebar', options: ['Left', 'Right'] }, - ].map((item, idx) => ( -
-
-
-
-

{item.title}

-

{item.desc}

-
-
- -
- ))} -
+
+

+ Editor +

+
+ {[ + { + icon: Monitor, + title: 'Default View', + desc: 'Choose default view for new files', + options: ['Split View', 'Editor Only', 'Preview Only'], + }, + { + icon: FolderOpen, + title: 'Sidebar Position', + desc: 'Change the location of the sidebar', + options: ['Left', 'Right'], + }, + ].map((item, idx) => ( +
+
+
+ +
+
+

+ {item.title} +

+

+ {item.desc} +

+
+
+ +
+ ))} +
-

System

-
- {[ - { icon: Globe, title: 'Language', desc: 'Change interface language', options: ['English', 'Spanish', 'French', 'German', 'Japanese'] }, - { icon: Bell, title: 'Notifications', desc: 'Configure desktop notifications', options: ['All', 'Important Only', 'None'] }, - ].map((item, idx) => ( -
-
-
-
-

{item.title}

-

{item.desc}

-
-
- -
- ))} -
-
-
- ) : ( -
-
-

Themes

-

Manage app appearance and customization

-
+

+ System +

+
+ {[ + { + icon: Globe, + title: 'Language', + desc: 'Change interface language', + options: [ + 'English', + 'Spanish', + 'French', + 'German', + 'Japanese', + ], + }, + { + icon: Bell, + title: 'Notifications', + desc: 'Configure desktop notifications', + options: ['All', 'Important Only', 'None'], + }, + ].map((item, idx) => ( +
+
+
+ +
+
+

+ {item.title} +

+

+ {item.desc} +

+
+
+ +
+ ))} +
+
+
+ ) : ( +
+
+

+ Themes +

+

+ Manage app appearance and customization +

+
-
-

Color Mode

-
- {[{ id: 'light', label: 'Light mode', icon: Sun }, { id: 'dark', label: 'Dark mode', icon: Moon }, { id: 'system', label: 'System', icon: Monitor }].map((mode) => ( - - ))} -
-
+
+

+ Color Mode +

+
+ {[ + { id: 'light', label: 'Light mode', icon: Sun }, + { id: 'dark', label: 'Dark mode', icon: Moon }, + { id: 'system', label: 'System', icon: Monitor }, + ].map((mode) => ( + + ))} +
+
-
-

Preset themes

-
- {THEME_PRESETS.map((theme) => { - const isActive = currentTheme === theme.value && !theme.disabled; - return ( - - ); - })} -
-
+
+

+ Preset themes +

+
+ {THEME_PRESETS.map((theme) => { + const isActive = + currentTheme === theme.value && !theme.disabled; + return ( + + ); + })} +
-
+
+ )} +
- ); +
+
+ ); } diff --git a/src/components/layout/editor/Editor.tsx b/src/components/layout/editor/Editor.tsx index 1102788..3a07e31 100644 --- a/src/components/layout/editor/Editor.tsx +++ b/src/components/layout/editor/Editor.tsx @@ -28,11 +28,10 @@ export function Editor({ style={{ backgroundColor: 'var(--editor-bg)' }} > {!activeFileId || - activeFileId === 'welcome' || - activeFileId.startsWith('cinder-') ? ( + activeFileId === 'welcome' || + activeFileId.startsWith('cinder-') ? ( /* --- SYSTEM TABS & EMPTY STATE --- */
- {activeFileId === 'cinder-settings' && } {activeFileId === 'cinder-info' && } diff --git a/src/components/layout/editor/EditorTabs.tsx b/src/components/layout/editor/EditorTabs.tsx index 8bbdef7..6bc864e 100644 --- a/src/components/layout/editor/EditorTabs.tsx +++ b/src/components/layout/editor/EditorTabs.tsx @@ -6,7 +6,6 @@ import { Gift, Maximize2, Minimize2, - Info, Settings, } from 'lucide-react'; @@ -55,7 +54,6 @@ export function EditorTabs() { let tabName = ''; if (isWelcomeTab) tabName = 'Welcome'; else if (isBlankTab) tabName = 'Untitled'; - else if (fileId === 'cinder-settings') tabName = 'Settings'; else if (fileId === 'cinder-info') tabName = 'About'; else tabName = file?.name.replace(/\.md$/, '') || 'Unknown'; @@ -118,7 +116,6 @@ export function EditorTabs() { color: isActive ? 'var(--editor-header-accent)' : 'inherit', }} > - {fileId === 'cinder-settings' && } {fileId === 'cinder-info' && } diff --git a/src/theme/cinderTheme.ts b/src/theme/cinderTheme.ts index 459b4bd..4dc52b9 100644 --- a/src/theme/cinderTheme.ts +++ b/src/theme/cinderTheme.ts @@ -38,9 +38,9 @@ const cinderEditorTheme = EditorView.theme( borderLeftWidth: '2px', }, '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': - { - backgroundColor: 'var(--editor-selection-bg) !important', - }, + { + backgroundColor: 'var(--editor-selection-bg) !important', + }, '.cm-activeLine': { backgroundColor: 'var(--bg-active)', }, diff --git a/src/theme/markdown.css b/src/theme/markdown.css index ebf0a06..4434c55 100644 --- a/src/theme/markdown.css +++ b/src/theme/markdown.css @@ -15,7 +15,7 @@ } /* Vertical rhythm */ -.markdown-preview>*+* { +.markdown-preview > * + * { margin-top: 1.2em; } @@ -109,8 +109,8 @@ white-space: pre-wrap; } -.markdown-preview ul>li::marker, -.markdown-preview ol>li::marker { +.markdown-preview ul > li::marker, +.markdown-preview ol > li::marker { color: var(--markdown-list); } @@ -267,7 +267,7 @@ padding: 0.5em 0; } -.markdown-preview .katex-display>.katex { +.markdown-preview .katex-display > .katex { font-size: 1.08em; } @@ -293,4 +293,4 @@ .markdown-preview::-webkit-scrollbar-thumb:hover { background: var(--border-primary); -} \ No newline at end of file +} From 48efe119e13ad59b62c0c3a96cb51ce894770f6e Mon Sep 17 00:00:00 2001 From: Naman Rai Date: Sun, 8 Mar 2026 00:24:18 +0530 Subject: [PATCH 3/5] fixed for prettier2 --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index a92130e..752f04f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "tauri:dev": "npx tauri dev", "tauri:build": "npx tauri build", "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"", - "format:write": "prettier --write \"src/**/*.{ts,tsx,css}\"", "prepare": "husky" }, "dependencies": { From 77ec1632eeaa4fa545826850c4611fe2667ebbde Mon Sep 17 00:00:00 2001 From: Naman Rai Date: Sun, 8 Mar 2026 00:43:32 +0530 Subject: [PATCH 4/5] addressed issue #26, autosave on/off indicator --- src/components/layout/editor/Editor.tsx | 21 +++- src/components/layout/editor/EditorTabs.tsx | 30 +++-- src/store/useAppStore.ts | 128 +++++++++++++++++--- 3 files changed, 147 insertions(+), 32 deletions(-) diff --git a/src/components/layout/editor/Editor.tsx b/src/components/layout/editor/Editor.tsx index 3a07e31..c61e3d9 100644 --- a/src/components/layout/editor/Editor.tsx +++ b/src/components/layout/editor/Editor.tsx @@ -1,4 +1,4 @@ -import type { MutableRefObject } from 'react'; +import { useEffect, type MutableRefObject } from 'react'; import { useAppStore } from '../../../store/useAppStore'; import { MarkdownPreview } from './MarkdownPreview'; import { Eye, ChevronLeft, FileText, Save } from 'lucide-react'; @@ -20,7 +20,20 @@ export function Editor({ editorViewRef, onCursorChange, }: EditorProps) { - const { activeFileId, activeFileContent, updateFileContent } = useAppStore(); + const { activeFileId, activeFileContent, updateFileContent, saveFile } = useAppStore(); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.ctrlKey || e.metaKey) && e.key === 's') { + e.preventDefault(); + if (activeFileId) { + saveFile(activeFileId); + } + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [activeFileId, saveFile]); return (
{!activeFileId || - activeFileId === 'welcome' || - activeFileId.startsWith('cinder-') ? ( + activeFileId === 'welcome' || + activeFileId.startsWith('cinder-') ? ( /* --- SYSTEM TABS & EMPTY STATE --- */
{activeFileId === 'cinder-settings' && } diff --git a/src/components/layout/editor/EditorTabs.tsx b/src/components/layout/editor/EditorTabs.tsx index 6bc864e..b8d7ba8 100644 --- a/src/components/layout/editor/EditorTabs.tsx +++ b/src/components/layout/editor/EditorTabs.tsx @@ -32,6 +32,7 @@ export function EditorTabs() { createFolder, closeOtherFiles, closeAllFiles, + dirtyFiles, } = useAppStore(); return ( @@ -127,18 +128,23 @@ export function EditorTabs() { {tabName} - +
+
+ +
); })} diff --git a/src/store/useAppStore.ts b/src/store/useAppStore.ts index af8c898..8d16ecf 100644 --- a/src/store/useAppStore.ts +++ b/src/store/useAppStore.ts @@ -1,5 +1,6 @@ import { create } from 'zustand'; import { invoke } from '@tauri-apps/api/core'; +import { ask } from '@tauri-apps/plugin-dialog'; import type { FileNode } from '../types/fileSystem'; export interface SearchResult { @@ -24,6 +25,7 @@ interface AppState { expandedFolderIds: string[]; // List of folder IDs that are expanded pendingFileId: string | null; isAutoSave: boolean; + dirtyFiles: Set; // Search State isSearchOpen: boolean; @@ -43,7 +45,8 @@ interface AppState { // Actions selectFile: (fileId: string) => void; openFileInNewTab: (fileId: string) => void; - closeFile: (fileId: string) => void; + closeFile: (fileId: string) => Promise; + saveFile: (fileId?: string) => Promise; updateFileContent: (fileId: string, content: string) => void; findFile: (id: string, nodes?: FileNode[]) => FileNode | null; getFileBreadcrumb: (fileId: string) => FileNode[]; @@ -77,8 +80,8 @@ interface AppState { duplicateFile: (fileId: string) => void; createFileInFolder: (folderId: string) => void; createFolder: (parentFolderId?: string | null) => void; - closeOtherFiles: (fileId: string) => void; - closeAllFiles: () => void; + closeOtherFiles: (fileId: string) => Promise; + closeAllFiles: () => Promise; } export const useAppStore = create((set, get) => ({ @@ -95,6 +98,7 @@ export const useAppStore = create((set, get) => ({ lastSidebarWidth: 20, expandedFolderIds: [], isAutoSave: true, + dirtyFiles: new Set(), isSearchOpen: false, searchQuery: '', @@ -344,10 +348,22 @@ export const useAppStore = create((set, get) => ({ } }, - closeFile: (fileId: string) => { + closeFile: async (fileId: string) => { + const state = get(); + if (state.dirtyFiles.has(fileId)) { + const confirmed = await ask( + 'You have unsaved changes. Are you sure you want to close without saving?', + { title: 'Unsaved Changes', kind: 'warning' } + ); + if (!confirmed) return; + } + const { openFiles, activeFileId } = get(); const newOpenFiles = openFiles.filter((id) => id !== fileId); + const newDirtyFiles = new Set(get().dirtyFiles); + newDirtyFiles.delete(fileId); + if (activeFileId === fileId) { const nextActive = newOpenFiles.length > 0 ? newOpenFiles[newOpenFiles.length - 1] : null; @@ -355,12 +371,31 @@ export const useAppStore = create((set, get) => ({ get().selectFile(nextActive); } else { set({ activeFileId: null, activeFileContent: '' }); - // If we closed the last tab, we might want to show empty state or Welcome? - // For now, empty state is fine. } } - set({ openFiles: newOpenFiles }); + set({ openFiles: newOpenFiles, dirtyFiles: newDirtyFiles }); + }, + + saveFile: async (fileId?: string) => { + const state = get(); + const targetFileId = fileId || state.activeFileId; + if (!targetFileId) return; + + const file = state.findFile(targetFileId); + const contentToSave = targetFileId === state.activeFileId ? state.activeFileContent : (file?.content || ''); + + if (file && file.path) { + try { + await invoke('write_note', { path: file.path, content: contentToSave }); + console.log('File saved manually:', file.path); + const newDirty = new Set(get().dirtyFiles); + newDirty.delete(targetFileId); + set({ dirtyFiles: newDirty }); + } catch (err) { + console.error('Failed to save file:', err); + } + } }, updateFileContent: (fileId: string, content: string) => { @@ -408,20 +443,28 @@ export const useAppStore = create((set, get) => ({ }; // Create file on disk with content - invoke('write_note', { path: filePath, content }) - .then(() => console.log('File created on disk:', filePath)) - .catch((err) => console.error('Failed to create file:', err)); + if (state.isAutoSave) { + invoke('write_note', { path: filePath, content }) + .then(() => console.log('File created on disk:', filePath)) + .catch((err) => console.error('Failed to create file:', err)); + } const newFiles = [...files, newFile]; const newOpenFiles = openFiles.map((id) => id === fileId ? newFileId : id ); + const newDirtyFiles = new Set(state.dirtyFiles); + if (!state.isAutoSave) { + newDirtyFiles.add(newFileId); + } + set({ files: newFiles, activeFileId: newFileId, openFiles: newOpenFiles, activeFileContent: content, + dirtyFiles: newDirtyFiles, }); return; } @@ -432,13 +475,28 @@ export const useAppStore = create((set, get) => ({ // Write to disk if we have a path if (filePath) { - invoke('write_note', { path: filePath, content }) - .then(() => console.log('File saved:', filePath)) - .catch((err) => console.error('Failed to save file:', err)); + if (state.isAutoSave) { + invoke('write_note', { path: filePath, content }) + .then(() => { + console.log('File saved:', filePath); + const stateNow = get(); + if (stateNow.dirtyFiles.has(fileId)) { + const newDirty = new Set(stateNow.dirtyFiles); + newDirty.delete(fileId); + set({ dirtyFiles: newDirty }); + } + }) + .catch((err) => console.error('Failed to save file:', err)); + } } // Normal update - Persist to store AND active state set((state) => { + const newDirty = new Set(state.dirtyFiles); + if (!state.isAutoSave) { + newDirty.add(fileId); + } + const updateContentRecursive = (nodes: FileNode[]): FileNode[] => { return nodes.map((node) => { if (node.id === fileId) { @@ -455,6 +513,7 @@ export const useAppStore = create((set, get) => ({ activeFileContent: state.activeFileId === fileId ? content : state.activeFileContent, files: updateContentRecursive(state.files), + dirtyFiles: newDirty, }; }); }, @@ -908,7 +967,15 @@ export const useAppStore = create((set, get) => ({ }, toggleAutoSave: () => { - set((state) => ({ isAutoSave: !state.isAutoSave })); + set((state) => { + const newAutoSave = !state.isAutoSave; + if (newAutoSave && state.dirtyFiles.size > 0) { + Array.from(state.dirtyFiles).forEach(fileId => { + get().saveFile(fileId); + }); + } + return { isAutoSave: newAutoSave }; + }); }, // --- Context Menu Actions --- @@ -1255,24 +1322,53 @@ export const useAppStore = create((set, get) => ({ } }, - closeOtherFiles: (fileId: string) => { + closeOtherFiles: async (fileId: string) => { const state = get(); + const otherDirtyFiles = Array.from(state.dirtyFiles).filter(id => id !== fileId && state.openFiles.includes(id)); + if (otherDirtyFiles.length > 0) { + const confirmed = await ask( + 'You have unsaved changes in other files. Are you sure you want to close them without saving?', + { title: 'Unsaved Changes', kind: 'warning' } + ); + if (!confirmed) return; + } + + const newDirtyFiles = new Set(state.dirtyFiles); + state.openFiles.forEach(id => { + if (id !== fileId) newDirtyFiles.delete(id); + }); + set({ openFiles: state.openFiles.includes(fileId) ? [fileId] : [], activeFileId: state.openFiles.includes(fileId) ? fileId : null, activeFileContent: state.activeFileId === fileId ? state.activeFileContent : '', + dirtyFiles: newDirtyFiles, }); if (state.activeFileId !== fileId && state.openFiles.includes(fileId)) { get().selectFile(fileId); } }, - closeAllFiles: () => { + closeAllFiles: async () => { + const state = get(); + const dirtyOpenFiles = Array.from(state.dirtyFiles).filter(id => state.openFiles.includes(id)); + if (dirtyOpenFiles.length > 0) { + const confirmed = await ask( + 'You have unsaved changes. Are you sure you want to close all files without saving?', + { title: 'Unsaved Changes', kind: 'warning' } + ); + if (!confirmed) return; + } + + const newDirtyFiles = new Set(state.dirtyFiles); + state.openFiles.forEach(id => newDirtyFiles.delete(id)); + set({ openFiles: [], activeFileId: null, activeFileContent: '', + dirtyFiles: newDirtyFiles, }); }, })); From 5e4c4661e91ebda8d362397a86c05aaec865af8e Mon Sep 17 00:00:00 2001 From: Naman Rai Date: Sun, 8 Mar 2026 00:59:44 +0530 Subject: [PATCH 5/5] addressed prettier issue --- src/components/layout/editor/Editor.tsx | 7 ++++--- src/store/useAppStore.ts | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/components/layout/editor/Editor.tsx b/src/components/layout/editor/Editor.tsx index c61e3d9..888debe 100644 --- a/src/components/layout/editor/Editor.tsx +++ b/src/components/layout/editor/Editor.tsx @@ -20,7 +20,8 @@ export function Editor({ editorViewRef, onCursorChange, }: EditorProps) { - const { activeFileId, activeFileContent, updateFileContent, saveFile } = useAppStore(); + const { activeFileId, activeFileContent, updateFileContent, saveFile } = + useAppStore(); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -41,8 +42,8 @@ export function Editor({ style={{ backgroundColor: 'var(--editor-bg)' }} > {!activeFileId || - activeFileId === 'welcome' || - activeFileId.startsWith('cinder-') ? ( + activeFileId === 'welcome' || + activeFileId.startsWith('cinder-') ? ( /* --- SYSTEM TABS & EMPTY STATE --- */
{activeFileId === 'cinder-settings' && } diff --git a/src/store/useAppStore.ts b/src/store/useAppStore.ts index 8d16ecf..2cb0499 100644 --- a/src/store/useAppStore.ts +++ b/src/store/useAppStore.ts @@ -383,7 +383,10 @@ export const useAppStore = create((set, get) => ({ if (!targetFileId) return; const file = state.findFile(targetFileId); - const contentToSave = targetFileId === state.activeFileId ? state.activeFileContent : (file?.content || ''); + const contentToSave = + targetFileId === state.activeFileId + ? state.activeFileContent + : file?.content || ''; if (file && file.path) { try { @@ -970,7 +973,7 @@ export const useAppStore = create((set, get) => ({ set((state) => { const newAutoSave = !state.isAutoSave; if (newAutoSave && state.dirtyFiles.size > 0) { - Array.from(state.dirtyFiles).forEach(fileId => { + Array.from(state.dirtyFiles).forEach((fileId) => { get().saveFile(fileId); }); } @@ -1324,7 +1327,9 @@ export const useAppStore = create((set, get) => ({ closeOtherFiles: async (fileId: string) => { const state = get(); - const otherDirtyFiles = Array.from(state.dirtyFiles).filter(id => id !== fileId && state.openFiles.includes(id)); + const otherDirtyFiles = Array.from(state.dirtyFiles).filter( + (id) => id !== fileId && state.openFiles.includes(id) + ); if (otherDirtyFiles.length > 0) { const confirmed = await ask( 'You have unsaved changes in other files. Are you sure you want to close them without saving?', @@ -1334,7 +1339,7 @@ export const useAppStore = create((set, get) => ({ } const newDirtyFiles = new Set(state.dirtyFiles); - state.openFiles.forEach(id => { + state.openFiles.forEach((id) => { if (id !== fileId) newDirtyFiles.delete(id); }); @@ -1352,7 +1357,9 @@ export const useAppStore = create((set, get) => ({ closeAllFiles: async () => { const state = get(); - const dirtyOpenFiles = Array.from(state.dirtyFiles).filter(id => state.openFiles.includes(id)); + const dirtyOpenFiles = Array.from(state.dirtyFiles).filter((id) => + state.openFiles.includes(id) + ); if (dirtyOpenFiles.length > 0) { const confirmed = await ask( 'You have unsaved changes. Are you sure you want to close all files without saving?', @@ -1362,7 +1369,7 @@ export const useAppStore = create((set, get) => ({ } const newDirtyFiles = new Set(state.dirtyFiles); - state.openFiles.forEach(id => newDirtyFiles.delete(id)); + state.openFiles.forEach((id) => newDirtyFiles.delete(id)); set({ openFiles: [],