diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index f08e282..ca94bd9 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -1554,6 +1554,7 @@ mod tests { sidebar_collapsed: true, top_bars_collapsed: false, status_bar_collapsed: true, + reduce_motion: true, line_numbers_visible: false, text_wrapping_enabled: false, syntax_highlighting_enabled: false, @@ -1591,6 +1592,7 @@ mod tests { drop(conn); let loaded = store.ui_layout_get().unwrap(); + assert!(!loaded.reduce_motion); assert!(loaded.line_numbers_visible); assert!(loaded.text_wrapping_enabled); assert!(loaded.syntax_highlighting_enabled); diff --git a/crates/store/src/settings.rs b/crates/store/src/settings.rs index 75c8217..1097282 100644 --- a/crates/store/src/settings.rs +++ b/crates/store/src/settings.rs @@ -74,6 +74,8 @@ pub struct UiLayoutSettings { pub sidebar_collapsed: bool, pub top_bars_collapsed: bool, pub status_bar_collapsed: bool, + #[serde(default)] + pub reduce_motion: bool, #[serde(default = "default_true")] pub line_numbers_visible: bool, #[serde(default = "default_true")] @@ -100,6 +102,7 @@ impl Default for UiLayoutSettings { sidebar_collapsed: false, top_bars_collapsed: false, status_bar_collapsed: false, + reduce_motion: false, line_numbers_visible: true, text_wrapping_enabled: true, syntax_highlighting_enabled: true, diff --git a/src/App.css b/src/App.css index b4deeae..7a445cb 100644 --- a/src/App.css +++ b/src/App.css @@ -1,262 +1,7 @@ @import "tailwindcss"; @plugin "@egoist/tailwindcss-icons"; @plugin "@tailwindcss/forms"; - -@font-face { - font-family: "Writer IBM Plex Mono"; - src: url("/fonts/ibm-plex-mono-400-normal.ttf") format("truetype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Mono"; - src: url("/fonts/ibm-plex-mono-400-italic.ttf") format("truetype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Mono"; - src: url("/fonts/ibm-plex-mono-700-normal.ttf") format("truetype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Mono"; - src: url("/fonts/ibm-plex-mono-700-italic.ttf") format("truetype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Sans"; - src: url("/fonts/ibm-plex-sans-400-normal.ttf") format("truetype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Sans"; - src: url("/fonts/ibm-plex-sans-400-italic.ttf") format("truetype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Sans"; - src: url("/fonts/ibm-plex-sans-700-normal.ttf") format("truetype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Sans"; - src: url("/fonts/ibm-plex-sans-700-italic.ttf") format("truetype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Serif"; - src: url("/fonts/ibm-plex-serif-400-normal.ttf") format("truetype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Serif"; - src: url("/fonts/ibm-plex-serif-400-italic.ttf") format("truetype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Serif"; - src: url("/fonts/ibm-plex-serif-700-normal.ttf") format("truetype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer IBM Plex Serif"; - src: url("/fonts/ibm-plex-serif-700-italic.ttf") format("truetype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Argon"; - src: url("/fonts/monaspace-argon-400-normal.otf") format("opentype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Argon"; - src: url("/fonts/monaspace-argon-400-italic.otf") format("opentype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Argon"; - src: url("/fonts/monaspace-argon-700-normal.otf") format("opentype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Argon"; - src: url("/fonts/monaspace-argon-700-italic.otf") format("opentype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Krypton"; - src: url("/fonts/monaspace-krypton-400-normal.otf") format("opentype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Krypton"; - src: url("/fonts/monaspace-krypton-400-italic.otf") format("opentype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Krypton"; - src: url("/fonts/monaspace-krypton-700-normal.otf") format("opentype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Krypton"; - src: url("/fonts/monaspace-krypton-700-italic.otf") format("opentype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Neon"; - src: url("/fonts/monaspace-neon-400-normal.otf") format("opentype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Neon"; - src: url("/fonts/monaspace-neon-400-italic.otf") format("opentype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Neon"; - src: url("/fonts/monaspace-neon-700-normal.otf") format("opentype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Neon"; - src: url("/fonts/monaspace-neon-700-italic.otf") format("opentype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Radon"; - src: url("/fonts/monaspace-radon-400-normal.otf") format("opentype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Radon"; - src: url("/fonts/monaspace-radon-400-italic.otf") format("opentype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Radon"; - src: url("/fonts/monaspace-radon-700-normal.otf") format("opentype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Radon"; - src: url("/fonts/monaspace-radon-700-italic.otf") format("opentype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Xenon"; - src: url("/fonts/monaspace-xenon-400-normal.otf") format("opentype"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Xenon"; - src: url("/fonts/monaspace-xenon-400-italic.otf") format("opentype"); - font-style: italic; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Xenon"; - src: url("/fonts/monaspace-xenon-700-normal.otf") format("opentype"); - font-style: normal; - font-weight: 700; - font-display: swap; -} - -@font-face { - font-family: "Writer Monaspace Xenon"; - src: url("/fonts/monaspace-xenon-700-italic.otf") format("opentype"); - font-style: italic; - font-weight: 700; - font-display: swap; -} +@import "./styles/fonts.css"; @theme { --font-sans: @@ -269,15 +14,18 @@ "Writer IBM Plex Mono", "IBM Plex Mono", "SF Mono", Monaco, "Cascadia Code", monospace; + /* FIXME: Remove in favor of tw built-ins */ --font-weight-light: 300; --font-weight-regular: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; + /* FIXME: remove bg prefix */ --color-bg-primary: #161616; --color-bg-hover: #262626; --color-bg-active: #393939; + --color-layer-01: #262626; --color-layer-02: #393939; --color-layer-03: #525252; @@ -289,10 +37,13 @@ --color-field-02: #262626; --color-field-hover-01: #262626; --color-field-hover-02: #393939; + + /* FIXME: remove border prefix */ --color-border-subtle: #393939; --color-border-strong: #525252; --color-border-inverse: #ffffff; --color-border-interactive: #33b1ff; + --color-text-primary: #f2f4f8; --color-text-secondary: #dde1e6; --color-text-placeholder: #525252; @@ -344,6 +95,7 @@ --weight-preview-heading: 600; --weight-preview-table-heading: 600; + /* FIXME: remove in favor of tw built-ins */ --radius-none: 0; --radius-sm: 2px; --radius-md: 4px; @@ -352,6 +104,7 @@ --radius-2xl: 12px; --radius-full: 9999px; + /* FIXME: remove in favor of tw built-ins */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.4); --shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.5); diff --git a/src/App.tsx b/src/App.tsx index 06d03c6..2fdbcfd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,3 @@ -import { Button } from "$components/Button"; import { AppHeaderBar } from "./components/layout/AppHeaderBar"; import { BackendAlerts } from "./components/layout/BackendAlerts"; import { FocusModePanel } from "./components/layout/FocusModePanel"; @@ -9,15 +8,6 @@ import { PdfExportDialog } from "./components/pdf/ExportDialog/ExportDialog"; import { useAppChromeController } from "./hooks/controllers/useAppChromeController"; import { useWorkspaceViewController } from "./hooks/controllers/useWorkspaceViewController"; -const ShowButton = ({ clickHandler, title, label }: { clickHandler: () => void; title: string; label: string }) => ( - -); - const AppContent = ({ isFocusMode }: { isFocusMode: boolean }) => { const { workspacePanelProps, focusModePanelProps, handleExportPdf } = useWorkspaceViewController(); @@ -37,20 +27,12 @@ const AppContent = ({ isFocusMode }: { isFocusMode: boolean }) => { }; function App() { - const { theme, isFocusMode, isSidebarCollapsed, showToggleControls, handleShowSidebar } = useAppChromeController(); + const { theme, isFocusMode } = useAppChromeController(); return (
- {!isFocusMode && showToggleControls && ( -
- {isSidebarCollapsed && ( - - )} -
- )} - {isFocusMode ? null : }
diff --git a/src/__tests__/ports.test.ts b/src/__tests__/ports.test.ts index 560b741..7dfbdc3 100644 --- a/src/__tests__/ports.test.ts +++ b/src/__tests__/ports.test.ts @@ -758,6 +758,7 @@ describe("ui layout Commands", () => { sidebar_collapsed: true, top_bars_collapsed: false, status_bar_collapsed: true, + reduce_motion: false, line_numbers_visible: false, text_wrapping_enabled: true, syntax_highlighting_enabled: false, @@ -779,6 +780,7 @@ describe("ui layout Commands", () => { sidebar_collapsed: true, top_bars_collapsed: false, status_bar_collapsed: true, + reduce_motion: false, line_numbers_visible: false, text_wrapping_enabled: true, syntax_highlighting_enabled: false, diff --git a/src/components/Sidebar/DocumentItem.tsx b/src/components/Sidebar/DocumentItem.tsx index 83a52ab..61d1c23 100644 --- a/src/components/Sidebar/DocumentItem.tsx +++ b/src/components/Sidebar/DocumentItem.tsx @@ -1,8 +1,10 @@ import { FileTextIcon } from "$icons"; import type { DocMeta } from "$types"; -import { useCallback, useMemo } from "react"; +import { useCallback } from "react"; import { TreeItem } from "./TreeItem"; +const fileTextIcon = { Component: FileTextIcon, size: "sm" as const }; + export function DocumentItem( { doc, isSelected, selectedDocPath, onSelectDocument, id }: { doc: DocMeta; @@ -12,12 +14,11 @@ export function DocumentItem( id: number; }, ) { - const fileTextIcon = useMemo(() => ({ Component: FileTextIcon, size: "sm" as const }), []); const handleClick = useCallback(() => onSelectDocument(id, doc.rel_path), [id, onSelectDocument, doc.rel_path]); return ( void; hideTitle: boolean }) => ( +const AppTitle = ({ hideTitle }: { hideTitle: boolean }) => (
- +
{hideTitle ? null :

Writer

} ); @@ -16,8 +16,10 @@ const AppTitle = ({ onToggleSidebar, hideTitle }: { onToggleSidebar: () => void; const SearchRow = ( { onOpenSearch, + onToggleSidebar, onToggleTabBar, onToggleStatusBar, + sidebarCollapsed, tabBarCollapsed, statusBarCollapsed, iconOnly, @@ -25,73 +27,95 @@ const SearchRow = ( compactTabLabel, }: { onOpenSearch: () => void; + onToggleSidebar: () => void; onToggleTabBar: () => void; onToggleStatusBar: () => void; + sidebarCollapsed: boolean; tabBarCollapsed: boolean; statusBarCollapsed: boolean; iconOnly: boolean; showSearchShortcut: boolean; compactTabLabel: boolean; }, -) => ( -
- +) => { + const statusbarId = useMemo(() => { + if (compactTabLabel) { + return { + label: statusBarCollapsed ? "Show Status" : "Hide Status", + title: statusBarCollapsed ? "Show Status" : "Hide Status", + }; + } + return { + label: statusBarCollapsed ? "Show Status Bar" : "Hide Status Bar", + title: statusBarCollapsed ? "Show Status Bar" : "Hide Status Bar", + }; + }, [compactTabLabel, statusBarCollapsed]); - + const tabbarId = useMemo(() => { + const title = `${tabBarCollapsed ? "Show" : "Hide"} tab bar (Ctrl+Shift+B)`; + if (compactTabLabel) { + return { label: tabBarCollapsed ? "Show Tabs" : "Hide Tabs", title }; + } + return { label: tabBarCollapsed ? "Show Tab Bar" : "Hide Tab Bar", title }; + }, [compactTabLabel, tabBarCollapsed]); + + const sidebarId = useMemo(() => { + const title = `${sidebarCollapsed ? "Show" : "Hide"} sidebar (Ctrl+B)`; + const label = sidebarCollapsed ? "Show Sidebar" : "Hide Sidebar"; + return { label, title }; + }, [sidebarCollapsed]); - -
-); + + + + + + + + + ); +}; export const AppHeaderBar = () => { const { + sidebarCollapsed, tabBarCollapsed, statusBarCollapsed, toggleSidebarCollapsed, @@ -100,21 +124,22 @@ export const AppHeaderBar = () => { setShowSearch, } = useAppHeaderBarState(); const { viewportWidth, isCompact, isNarrow } = useViewportTier(); + const showSearchShortcut = useMemo(() => viewportWidth >= 1240, [viewportWidth]); + const iconOnly = useMemo(() => viewportWidth < 760, [viewportWidth]); const handleOpenSearch = useCallback(() => { setShowSearch(true); }, [setShowSearch]); - const showSearchShortcut = useMemo(() => viewportWidth >= 1240, [viewportWidth]); - const iconOnly = useMemo(() => viewportWidth < 760, [viewportWidth]); - return (
- + + {isVisible && ( + + {children} + + )} + + ); +} + export function WorkspacePanel({ toolbar, editor, preview, statusBar, calmUiVisibility }: WorkspacePanelProps) { + const skipAnimation = useSkipAnimation(); const { viewportWidth } = useViewportTier(FALLBACK_VIEWPORT_WIDTH); const { sidebarCollapsed } = useWorkspacePanelSidebarState(); const { isSplitView, isPreviewVisible } = useWorkspacePanelModeState(); @@ -178,30 +212,69 @@ export function WorkspacePanel({ toolbar, editor, preview, statusBar, calmUiVisi const sidebarStyle = useMemo(() => ({ width: `${sidebarWidth}px` }), [sidebarWidth]); + const sectionTransition = useMemo(() => skipAnimation ? NO_MOTION_TRANSITION : CHROME_SECTION_TRANSITION, [ + skipAnimation, + ]); + const newDocumentHandler = useMemo(() => toolbar.isNewDocumentDisabled ? void 0 : toolbar.onNewDocument, [ toolbar.isNewDocumentDisabled, toolbar.onNewDocument, ]); + const sidebarMotionProps = useMemo( + () => ({ + initial: { opacity: 0, x: -16 }, + animate: { opacity: 1, x: 0 }, + exit: { opacity: 0, x: -12 }, + transition: sectionTransition, + }), + [sectionTransition], + ); + + const tabBarMotionProps = useMemo( + () => ({ + initial: { opacity: 0, y: -8, height: 0 }, + animate: { opacity: 1, y: 0, height: "auto" }, + exit: { opacity: 0, y: -8, height: 0 }, + transition: sectionTransition, + }), + [sectionTransition], + ); + + const statusBarMotionProps = useMemo( + () => ({ + initial: { opacity: 0, y: 8, height: 0 }, + animate: { opacity: 1, y: 0, height: "auto" }, + exit: { opacity: 0, y: 8, height: 0 }, + transition: sectionTransition, + }), + [sectionTransition], + ); + return (
- {effectiveSidebarVisible && ( -
- -
-
- )} +
+ +
+
+ {/* oxlint-disable react/jsx-max-depth */}
- {effectiveTabBarVisible && } +
+ +
- {effectiveStatusBarVisible && } + +
+ +
+ {/* oxlint-enable react/jsx-max-depth */}
); } diff --git a/src/editor/themes/oxocarbon-dark.ts b/src/editor/themes/oxocarbon-dark.ts index ea4716c..b1e62b6 100644 --- a/src/editor/themes/oxocarbon-dark.ts +++ b/src/editor/themes/oxocarbon-dark.ts @@ -35,10 +35,15 @@ export const oxocarbonDarkTheme = EditorView.theme({ "&": { backgroundColor: colors.background, color: colors.text, - fontSize: "14px", - fontFamily: "\"IBM Plex Mono\", \"SF Mono\", Monaco, monospace", + fontSize: "var(--editor-font-size, 14px)", + fontFamily: "var(--editor-font-family, \"IBM Plex Mono\", \"SF Mono\", Monaco, monospace)", + }, + ".cm-scroller": { fontFamily: "var(--editor-font-family, \"IBM Plex Mono\", \"SF Mono\", Monaco, monospace)" }, + ".cm-content": { + fontFamily: "var(--editor-font-family, \"IBM Plex Mono\", \"SF Mono\", Monaco, monospace)", + caretColor: colors.cyan, + padding: "16px 0", }, - ".cm-content": { caretColor: colors.cyan, padding: "16px 0" }, ".cm-cursor": { borderLeftColor: colors.cyan, borderLeftWidth: "2px" }, "&.cm-focused .cm-cursor": { borderLeftColor: colors.cyan }, "&.cm-focused .cm-selectionBackground": { backgroundColor: "rgba(51, 177, 255, 0.3)" }, diff --git a/src/editor/themes/oxocarbon-light.ts b/src/editor/themes/oxocarbon-light.ts index 1bae159..a758b16 100644 --- a/src/editor/themes/oxocarbon-light.ts +++ b/src/editor/themes/oxocarbon-light.ts @@ -35,10 +35,15 @@ export const oxocarbonLightTheme = EditorView.theme({ "&": { backgroundColor: colors.background, color: colors.text, - fontSize: "14px", - fontFamily: "\"IBM Plex Mono\", \"SF Mono\", Monaco, monospace", + fontSize: "var(--editor-font-size, 14px)", + fontFamily: "var(--editor-font-family, \"IBM Plex Mono\", \"SF Mono\", Monaco, monospace)", + }, + ".cm-scroller": { fontFamily: "var(--editor-font-family, \"IBM Plex Mono\", \"SF Mono\", Monaco, monospace)" }, + ".cm-content": { + fontFamily: "var(--editor-font-family, \"IBM Plex Mono\", \"SF Mono\", Monaco, monospace)", + caretColor: colors.cyan, + padding: "16px 0", }, - ".cm-content": { caretColor: colors.cyan, padding: "16px 0" }, ".cm-cursor": { borderLeftColor: colors.cyan, borderLeftWidth: "2px" }, "&.cm-focused .cm-cursor": { borderLeftColor: colors.cyan }, "&.cm-focused .cm-selectionBackground": { backgroundColor: "rgba(15, 98, 254, 0.2)" }, diff --git a/src/hooks/app/useSettingsSync.ts b/src/hooks/app/useSettingsSync.ts index 3ef3cf4..36a259b 100644 --- a/src/hooks/app/useSettingsSync.ts +++ b/src/hooks/app/useSettingsSync.ts @@ -30,6 +30,7 @@ export function useSettingsSync(): void { state.setSidebarCollapsed(settings.sidebar_collapsed); state.setTopBarsCollapsed(settings.top_bars_collapsed); state.setStatusBarCollapsed(settings.status_bar_collapsed); + state.setReduceMotion(settings.reduce_motion); state.setLineNumbersVisible(settings.line_numbers_visible); state.setTextWrappingEnabled(settings.text_wrapping_enabled); state.setSyntaxHighlightingEnabled(settings.syntax_highlighting_enabled); diff --git a/src/hooks/controllers/useWorkspaceViewController.ts b/src/hooks/controllers/useWorkspaceViewController.ts index d9301ef..1882613 100644 --- a/src/hooks/controllers/useWorkspaceViewController.ts +++ b/src/hooks/controllers/useWorkspaceViewController.ts @@ -12,7 +12,7 @@ import { usePdfExport, usePdfExportUI } from "$hooks/usePdfExport"; import { usePreview } from "$hooks/usePreview"; import { useWorkspaceSync } from "$hooks/useWorkspaceSync"; import type { PdfExportOptions } from "$pdf/types"; -import { useCalmUiSettings, useEditorPresentationStateRaw } from "$state/selectors"; +import { useEditorPresentationStateRaw } from "$state/selectors"; import type { DocRef, Maybe } from "$types"; import { useMemo } from "react"; @@ -40,7 +40,6 @@ export function useWorkspaceViewController(): WorkspaceViewController { useWorkspaceSync(); useLayoutHotkeys(); - const calmUiSettings = useCalmUiSettings(); const editorPresentation = useEditorPresentationStateRaw(); const { locations, @@ -145,14 +144,6 @@ export function useWorkspaceViewController(): WorkspaceViewController { useSettingsSync(); - const calmUiEffectiveVisibility = useMemo(() => { - if (!calmUiSettings.enabled || calmUiSettings.chromeTemporarilyVisible) { - return { sidebar: true, statusBar: true, tabBar: true }; - } - - return { sidebar: false, statusBar: false, tabBar: false }; - }, [calmUiSettings.chromeTemporarilyVisible, calmUiSettings.enabled]); - const toolbarProps = useMemo( () => ({ saveStatus: editorModel.saveStatus, @@ -222,14 +213,8 @@ export function useWorkspaceViewController(): WorkspaceViewController { ); const workspacePanelProps = useMemo( - () => ({ - toolbar: toolbarProps, - editor: editorProps, - preview: previewProps, - statusBar: statusBarProps, - calmUiVisibility: calmUiEffectiveVisibility, - }), - [toolbarProps, editorProps, previewProps, statusBarProps, calmUiEffectiveVisibility], + () => ({ toolbar: toolbarProps, editor: editorProps, preview: previewProps, statusBar: statusBarProps }), + [toolbarProps, editorProps, previewProps, statusBarProps], ); return { workspacePanelProps, focusModePanelProps, handleExportPdf }; diff --git a/src/ports/types.ts b/src/ports/types.ts index e4a8fcb..ad3bd3f 100644 --- a/src/ports/types.ts +++ b/src/ports/types.ts @@ -33,6 +33,7 @@ export type UiLayoutSettings = { sidebar_collapsed: boolean; top_bars_collapsed: boolean; status_bar_collapsed: boolean; + reduce_motion: boolean; line_numbers_visible: boolean; text_wrapping_enabled: boolean; syntax_highlighting_enabled: boolean; diff --git a/src/state/helpers.ts b/src/state/helpers.ts index 015ee58..39a9eb4 100644 --- a/src/state/helpers.ts +++ b/src/state/helpers.ts @@ -10,6 +10,7 @@ export const stateToLayoutSettings = ( sidebar_collapsed: layoutChrome.sidebarCollapsed, top_bars_collapsed: layoutChrome.topBarsCollapsed, status_bar_collapsed: layoutChrome.statusBarCollapsed, + reduce_motion: layoutChrome.reduceMotion, line_numbers_visible: presentation.lineNumbersVisible, text_wrapping_enabled: presentation.textWrappingEnabled, syntax_highlighting_enabled: presentation.syntaxHighlightingEnabled, diff --git a/src/state/selectors.ts b/src/state/selectors.ts index 75f4bf0..ae910bd 100644 --- a/src/state/selectors.ts +++ b/src/state/selectors.ts @@ -301,6 +301,7 @@ export const useLayoutSettingsWriterToolsState = () => export const useAppHeaderBarState = () => useLayoutStore( useShallow((state) => ({ + sidebarCollapsed: state.sidebarCollapsed, tabBarCollapsed: state.topBarsCollapsed, statusBarCollapsed: state.statusBarCollapsed, toggleSidebarCollapsed: state.toggleSidebarCollapsed, diff --git a/src/styles/fonts.css b/src/styles/fonts.css new file mode 100644 index 0000000..34d3c57 --- /dev/null +++ b/src/styles/fonts.css @@ -0,0 +1,255 @@ +@font-face { + font-family: "Writer IBM Plex Mono"; + src: url("/fonts/ibm-plex-mono-400-normal.ttf") format("truetype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Mono"; + src: url("/fonts/ibm-plex-mono-400-italic.ttf") format("truetype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Mono"; + src: url("/fonts/ibm-plex-mono-700-normal.ttf") format("truetype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Mono"; + src: url("/fonts/ibm-plex-mono-700-italic.ttf") format("truetype"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Sans"; + src: url("/fonts/ibm-plex-sans-400-normal.ttf") format("truetype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Sans"; + src: url("/fonts/ibm-plex-sans-400-italic.ttf") format("truetype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Sans"; + src: url("/fonts/ibm-plex-sans-700-normal.ttf") format("truetype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Sans"; + src: url("/fonts/ibm-plex-sans-700-italic.ttf") format("truetype"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Serif"; + src: url("/fonts/ibm-plex-serif-400-normal.ttf") format("truetype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Serif"; + src: url("/fonts/ibm-plex-serif-400-italic.ttf") format("truetype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Serif"; + src: url("/fonts/ibm-plex-serif-700-normal.ttf") format("truetype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer IBM Plex Serif"; + src: url("/fonts/ibm-plex-serif-700-italic.ttf") format("truetype"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Argon"; + src: url("/fonts/monaspace-argon-400-normal.otf") format("opentype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Argon"; + src: url("/fonts/monaspace-argon-400-italic.otf") format("opentype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Argon"; + src: url("/fonts/monaspace-argon-700-normal.otf") format("opentype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Argon"; + src: url("/fonts/monaspace-argon-700-italic.otf") format("opentype"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Krypton"; + src: url("/fonts/monaspace-krypton-400-normal.otf") format("opentype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Krypton"; + src: url("/fonts/monaspace-krypton-400-italic.otf") format("opentype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Krypton"; + src: url("/fonts/monaspace-krypton-700-normal.otf") format("opentype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Krypton"; + src: url("/fonts/monaspace-krypton-700-italic.otf") format("opentype"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Neon"; + src: url("/fonts/monaspace-neon-400-normal.otf") format("opentype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Neon"; + src: url("/fonts/monaspace-neon-400-italic.otf") format("opentype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Neon"; + src: url("/fonts/monaspace-neon-700-normal.otf") format("opentype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Neon"; + src: url("/fonts/monaspace-neon-700-italic.otf") format("opentype"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Radon"; + src: url("/fonts/monaspace-radon-400-normal.otf") format("opentype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Radon"; + src: url("/fonts/monaspace-radon-400-italic.otf") format("opentype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Radon"; + src: url("/fonts/monaspace-radon-700-normal.otf") format("opentype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Radon"; + src: url("/fonts/monaspace-radon-700-italic.otf") format("opentype"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Xenon"; + src: url("/fonts/monaspace-xenon-400-normal.otf") format("opentype"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Xenon"; + src: url("/fonts/monaspace-xenon-400-italic.otf") format("opentype"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Xenon"; + src: url("/fonts/monaspace-xenon-700-normal.otf") format("opentype"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Writer Monaspace Xenon"; + src: url("/fonts/monaspace-xenon-700-italic.otf") format("opentype"); + font-style: italic; + font-weight: 700; + font-display: swap; +}