@takazudo/app-defaults
Single source of truth for the zudotext app configuration schema, default values, and validation. Every consumer (Tauri app, scaffold tool, settings UI) imports types and defaults from this package.
Main Exports
// Types
import type {
AppSettings,
AppInstanceConfig,
FramesetTree,
HeaderLeftPin,
PinTemplate,
QuickAction,
QuickActionStep,
PinProviderPropsByProviderId,
DeviceOverride,
SyncSettings,
VimSettings,
ColorMode, // "system" | "light" | "dark" — general.colorMode (epic #5893)
} from "@takazudo/app-defaults";
// Color types re-exported from @takazudo/color-themes — see that package's docs
import type { ColorStructure, ColorSettings, ColorScheme, RampRef, Ramps, SemanticKey } from "@takazudo/color-themes";
// Default values
import {
defaultSettings,
defaultEditorSettings,
defaultVimSettings,
defaultShortcuts,
defaultLayoutSettings,
defaultGeneralSettings,
defaultColorStructure,
defaultResolvedColors,
defaultSyncSettings,
defaultActiveDraft,
defaultDraftCount,
defaultHeaderLeftPins,
defaultFramesetTree,
defaultDocCloudSettings,
defaultDirectoryViewSettings,
VIMRC_MAX_LENGTH,
COLOR_MODES, // readonly ["system", "light", "dark"] — the colorMode option list
} from "@takazudo/app-defaults";
// Validation
import { validateSettings } from "@takazudo/app-defaults";AppSettings Type
The AppSettings interface defines the full configuration shape stored in .zudotext.settings.json:
interface AppSettings {
general: {
colorScheme: string;
colorMode: ColorMode; // "system" | "light" | "dark" — see below
formatOnArchive: boolean;
windowOpacity: number; // 0.3–1.0
displayScale: number; // discrete steps: 0.75–2.0
appTitle?: string | null; // OS window title override (clamped to 64 chars)
disableSpotlightSearch: boolean; // fall through to native OS picker when true
showMinimap: boolean; // minimap rail in vertical timeline layout
};
color: ColorStructure; // { ramps, map } — see @takazudo/color-themes
activeDraft: number; // 1–99
draftCount: number; // 1–99
editor: {
vimMode: boolean;
fontFamily: string;
fontSize: number;
lineHeight: number;
paddingHorizontal: number;
paddingVertical: number;
typewriterScrolling: boolean;
showStatusBar: boolean;
markdownListIndent: boolean;
listHangingIndent: boolean;
showLineNumbers: boolean;
lineNumberLeftPadding: number; // gutter left padding, range 0–40 px
showIndentGuides: boolean;
indentType: "tab" | "spaces";
indentSize: number;
directiveCompletion: boolean;
inlineStyles: {
italic: { fontStyle: "normal" | "italic"; fontWeight: number };
emphasis: { fontStyle: "normal" | "italic"; fontWeight: number };
strong: { fontStyle: "normal" | "italic"; fontWeight: number };
};
};
vim: VimSettings;
fontCandidates: string[];
shortcuts: {
// Every shortcut value is string[] — multiple keys per action are supported.
// An empty array [] means unbound.
toggleEditorPreview: string[];
commandPalette: string[];
navInbox: string[];
navArchives: string[];
navSearch: string[];
navTags: string[]; // was navPins in old docs — the actual key is navTags
equalizeLayout: string[];
layoutResetSplit: string[];
toggleVimMode: string[];
openSettings: string[];
reloadApp: string[];
formatMarkdown: string[];
summarizedDraftList: string[];
toggleTodoView: string[];
toggleMindmapView: string[];
deleteArchive: string[];
printPreview: string[];
showShortcutPanel: string[];
draft1: string[];
// ... draft2 through draft9 (draft10 does not exist)
newDraft: string[];
previousDraft: string[];
nextDraft: string[];
splitFrameRight: string[];
splitFrameLeft: string[];
splitFrameTop: string[];
splitFrameBottom: string[];
focusFrameRight: string[];
focusFrameLeft: string[];
focusFrameTop: string[];
focusFrameBottom: string[];
kanbanMoveDown: string[];
kanbanMoveUp: string[];
kanbanMoveLeft: string[];
kanbanMoveRight: string[];
kanbanEditCard: string[];
kanbanNewCard: string[];
kanbanDeleteCard: string[];
kanbanToggleNotify: string[];
// Pile View shortcuts
pileView: string[];
togglePileViewDirection: string[];
togglePileViewFullContent: string[];
pileViewNext: string[];
pileViewPrev: string[];
pileViewOpenItem: string[];
pileViewArchiveItem: string[];
// Frame-level shortcuts (W5.5)
frameToggleZoom: string[];
frameCollapse: string[];
frameClose: string[];
frameNext: string[];
framePrev: string[];
frameSplitRight: string[];
frameSplitLeft: string[];
frameSplitTop: string[];
frameSplitBottom: string[];
// Open-provider shortcuts (W5.5)
// ... and many more; see AppSettings["shortcuts"] in types.ts for the full list
};
pileView: {
cardWidth: number; // card width in pixels, range 400–800, default 400
};
layout: {
sidebarPosition: "left" | "right";
draftBarCollapsed: boolean;
};
sync: SyncSettings;
/** Preferences only. Calling tokens are never settings. */
docCloud: {
defaultProjectSlug: string | null;
checkpointOnEdit: boolean;
publishNotifications: boolean;
serverOrigin: string;
};
/** Retained name: now the global width of EFE's embedded tree only. */
directoryView: { lastSidebarWidthPx: number };
subscription: SubscriptionInfo;
quickActions: { tiles: QuickAction[] };
/**
* Singleton frameset tree — the live layout for the current app window.
* Replaces the old framesets[]/currentFramesetId pair from Frameset Arch v1.
*/
frameset: FramesetTree;
/**
* Header pin entries displayed as icon buttons in the app toolbar.
* Each pin carries a structural template installed wholesale on activation.
*/
headerLeftPins: HeaderLeftPin[];
/**
* Per-leaf UI state cache, keyed by cache namespace then by leaf frameId.
* Default: {} (empty).
*/
framesetLeafState: Record<string, Record<string, JsonValue>>;
/**
* Most-recently-used ViewProvider ids for the empty-frame nav.
* Order is most-recent-first. Default: [].
*/
framesetRecentProviders: string[];
/** Only docCloud.defaultProjectSlug is in the Doc Cloud override surface. */
deviceOverrides: Record<string, DeviceOverride>;
}general.colorMode
general.colorMode is a ColorMode — COLOR_MODES gives the three values as ["system", "light", "dark"] as const. It is distinct from SchemeMode (@takazudo/color-themes), which has only the resolved two-valued "light" | "dark": "system" follows the OS appearance and is resolved down to a SchemeMode at runtime by the effective-mode store, not by this package. colorScheme still names which registry scheme is active (currently always "default" — see @takazudo/color-themes); colorMode picks which of that scheme's two palettes is displayed. validateSettings() enum-validates colorMode against COLOR_MODES and falls back to the default ("system") on an unknown value. colorScheme itself is only checked for being a string here — healing an unknown scheme name (e.g. a pre-restructure default-dark/default-light) is the Settings Doctor's job, not validateSettings()'s; see Settings Doctor.
HeaderLeftPin
interface HeaderLeftPin {
id: string; // stable kebab-case id, e.g. "inbox-default"
template: PinTemplate;
label: string; // display label and tooltip text
iconId: string; // Lucide icon name, e.g. "inbox", "archive"
routeSlug: string | null; // used for /p/:slug routes; null for built-in pins
visible: boolean;
providerProps?: Record<string, unknown>; // known shapes catalogued by PinProviderPropsByProviderId
}
interface PinTemplate {
tree: FramesetTree;
activeFrameId?: string;
}PinProviderPropsByProviderId["core.doc-cloud"] permits only projectSlug, initialSurface, editorLayout, and outlineCollapsed. Doc Cloud pins are one-leaf templates. PinProviderPropsByProviderId["core.external-file-editor"] permits initialFiles, initialTreeRoot, and treeCollapsed; the former Directory View is not a provider-props target.
QuickAction
type QuickActionStep = { commandId: string };
type QuickAction = {
id: string;
name: string;
iconId: string;
aliases: string[];
steps: QuickActionStep[];
};SyncSettings
interface SyncSettings {
// Cloud sync
cloudDeviceId: string;
cloudDeviceName: string;
cloudRealtimeEnabled: boolean;
}VimSettings
interface VimSettings {
clipboardSync: boolean;
showModeIndicator: boolean;
vimrc: string;
}Default Values
Editor
| Setting | Default |
|---|---|
vimMode | true |
fontFamily | "JetBrains Mono" |
fontSize | 16 |
lineHeight | 1.6 |
paddingHorizontal | 12 |
paddingVertical | 8 |
typewriterScrolling | false |
showStatusBar | true |
markdownListIndent | true |
listHangingIndent | true |
showLineNumbers | true |
lineNumberLeftPadding | 12 |
showIndentGuides | true |
indentType | "spaces" |
indentSize | 2 |
directiveCompletion | true |
inlineStyles.italic | { fontStyle: "italic", fontWeight: 400 } |
inlineStyles.emphasis | { fontStyle: "italic", fontWeight: 400 } |
inlineStyles.strong | { fontStyle: "normal", fontWeight: 700 } |
Vim
| Setting | Default |
|---|---|
clipboardSync | true |
showModeIndicator | true |
vimrc | "" |
Shortcuts
Every shortcut value is string[]. An empty array [] means the action is unbound. Multiple key chords can be assigned to the same action — all of them trigger it.
| Action | Default |
|---|---|
toggleEditorPreview | ["Mod+E"] |
commandPalette | ["Mod+K"] |
openSettings | ["Mod+,"] |
draft1–draft9 | ["Mod+1"]–["Mod+9"] |
newDraft | ["Mod+N"] |
previousDraft | ["Mod+Shift+["] |
nextDraft | ["Mod+Shift+]"] |
kanbanMoveDown / Up / Left / Right | ["J"] / ["K"] / ["H"] / ["L"] |
kanbanEditCard | ["E"] |
kanbanNewCard | ["N"] |
kanbanDeleteCard | ["D"] |
deleteArchive | ["Mod+Backspace"] |
showShortcutPanel | ["?"] |
mindmapMoveDown / Up | ["J"] / ["K"] |
mindmapCollapse / Expand | ["H"] / ["L"] |
mindmapAddChild | ["O"] |
mindmapAddSibling | ["A"] |
mindmapInsertParent | ["Alt+Tab"] |
mindmapOutdentNode | ["Shift+Tab"] |
mindmapDeleteNode | ["D"] |
mindmapEditNode | ["E"] |
frameNext | ["Ctrl+Tab"] |
framePrev | ["Ctrl+Shift+Tab"] |
frameSplitRight | ["Mod+Shift+Backslash"] |
Pile View shortcuts:
| Action | Default |
|---|---|
pileView | [] (unbound — assign in Settings → Shortcuts) |
togglePileViewDirection | [] (opt-in) |
togglePileViewFullContent | [] (opt-in) |
pileViewNext | ["J", "L", "ArrowRight", "ArrowDown"] |
pileViewPrev | ["K", "H", "ArrowLeft", "ArrowUp"] |
pileViewOpenItem | ["Enter"] |
pileViewArchiveItem | ["A"] |
Shortcuts for split frame operations, focus frame navigation, and most navigation keys (navInbox, navArchives, navSearch, navTags, equalizeLayout, layoutResetSplit, toggleVimMode, reloadApp, formatMarkdown, summarizedDraftList, toggleTodoView, toggleMindmapView, printPreview) all default to [] (unbound).
Layout
| Setting | Default |
|---|---|
sidebarPosition | "left" |
draftBarCollapsed | false |
General
| Setting | Default |
|---|---|
colorScheme | "default" |
colorMode | "system" |
formatOnArchive | false |
windowOpacity | 1.0 |
displayScale | 1.0 |
appTitle | null |
disableSpotlightSearch | false |
showMinimap | true |
Color
AppSettings["color"] defaults to defaultColorStructure, which is seedColorStructure(getSchemeByName(defaultThemeName)) — a deep clone of the default scheme's { ramps, modes: { light, dark } }. This is the persisted shape: since epic #5893 one scheme owns both appearances, so the block carries BOTH mode maps and validateColorStructure() rejects (and reseeds) anything that supplies only one.
A second export, defaultResolvedColors (= resolveScheme(getSchemeByName(defaultThemeName))), is the same default scheme already walked through every RampRef into concrete ColorSettings colors — used wherever a component needs resolved colors rather than the ramp structure (e.g. before the first applyColors() call). defaultColorSettings is a deprecated alias for defaultResolvedColors, kept only until call sites migrate.
Pile View
| Setting | Default |
|---|---|
cardWidth | 400 (range 400–800 px) |
Sync
| Setting | Default |
|---|---|
cloudDeviceId | "" |
cloudDeviceName | "" |
cloudRealtimeEnabled | true |
Doc Cloud
| Setting | Default |
|---|---|
defaultProjectSlug | null (open Projects) |
checkpointOnEdit | true |
publishNotifications | true |
serverOrigin | "" (use adapter origin) |
Credentials are intentionally absent. The only device-overridable Doc Cloud field is defaultProjectSlug; an explicit null override means Projects.
Embedded EFE tree width
defaultDirectoryViewSettings is { lastSidebarWidthPx: 240 }. The historical settings key remains, but it controls the tree pane inside External File Editor; there is no registered standalone Directory View provider.
Quick Actions
Default: { tiles: [] } (no quick actions)
Drafts
| Setting | Default |
|---|---|
activeDraft | 1 |
draftCount | 1 |
Font Candidates
Default list of font families including JetBrains Mono, Fira Code, Hack, Menlo, Monaco, SF Mono, and Nerd Font variants. Used by the FontPicker component.
validateSettings()
Validates and sanitizes an unknown input into a valid AppSettings object. Returns null if the input is not an object.
function validateSettings(settings: unknown): AppSettings | null;Used in both the settings context (on load) and the settings dialog (on save) to enforce valid configuration at all times.
Key behaviors:
Falls back to defaults — missing or invalid fields are replaced with defaults
Range validation — numeric fields are clamped (e.g., fontSize 10–24, lineHeight 1.0–2.0, windowOpacity 0.3–1.0, activeDraft/draftCount 1–99 integer, pileView.cardWidth 400–800, lineNumberLeftPadding 0–40)
String length limits — vimrc is reset to empty string if it exceeds 4096 characters; appTitle is clamped to 64 characters
Schema migration — handles renamed fields:
general.theme/general.colorSchema→general.colorSchemeactiveTab→activeDraft,tabCount→draftCountshortcuts.tab1–tab9→shortcuts.draft1–draft9shortcuts.draft10is deleted (Mod+0 is reserved for display scale reset)mindmap.depthColor*block is dropped (now controlled by color scheme tokens)
Color validation —
coloris validated as a wholeColorStructure: ramp array lengths (base5,accent3), every color string (hex,oklch(...), ortransparent), and everyRampRef(a literal color string, or a{ base | accent: n }index within range /{ state: role }) must all be structurally valid, or the entirecolorfield is discarded and re-seeded fromdefaultColorStructure— there is no per-field fallback the way other settings sections workFrameset validation — validates
framesettree structure; falls back todefaultFramesetTreeon invalid input; wipesframesetLeafStateon v1→v2 migrationDoc Cloud validation — validates the default-project slug, booleans, and bare server origin; drops credential-like unknown fields; validates Doc Cloud pin props to the four safe seeds
Retired-provider no-migration behavior — provider ids are opaque. Old pre-release Directory View pins are preserved without a compatibility reader and may show provider-not-found until the user removes them or resets settings
Vim settings validation — validates
clipboardSync,showModeIndicator(booleans), andvimrc(string with length cap)
Dependencies
@takazudo/color-themes— used to generate default color values from the default theme@takazudo/shortcut-engine— used for shortcut normalization and deduplication invalidateSettings@takazudo/view-provider— providesFramesetTreetype andvalidateFramesetTreeused by validation