zudo-text

検索したい単語を入力

いつでも検索バーを開ける

@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 ColorModeCOLOR_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

SettingDefault
vimModetrue
fontFamily"JetBrains Mono"
fontSize16
lineHeight1.6
paddingHorizontal12
paddingVertical8
typewriterScrollingfalse
showStatusBartrue
markdownListIndenttrue
listHangingIndenttrue
showLineNumberstrue
lineNumberLeftPadding12
showIndentGuidestrue
indentType"spaces"
indentSize2
directiveCompletiontrue
inlineStyles.italic{ fontStyle: "italic", fontWeight: 400 }
inlineStyles.emphasis{ fontStyle: "italic", fontWeight: 400 }
inlineStyles.strong{ fontStyle: "normal", fontWeight: 700 }

Vim

SettingDefault
clipboardSynctrue
showModeIndicatortrue
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.

ActionDefault
toggleEditorPreview["Mod+E"]
commandPalette["Mod+K"]
openSettings["Mod+,"]
draft1draft9["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:

ActionDefault
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

SettingDefault
sidebarPosition"left"
draftBarCollapsedfalse

General

SettingDefault
colorScheme"default"
colorMode"system"
formatOnArchivefalse
windowOpacity1.0
displayScale1.0
appTitlenull
disableSpotlightSearchfalse
showMinimaptrue

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

SettingDefault
cardWidth400 (range 400–800 px)

Sync

SettingDefault
cloudDeviceId""
cloudDeviceName""
cloudRealtimeEnabledtrue

Doc Cloud

SettingDefault
defaultProjectSlugnull (open Projects)
checkpointOnEdittrue
publishNotificationstrue
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

SettingDefault
activeDraft1
draftCount1

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.colorSchemageneral.colorScheme

    • activeTabactiveDraft, tabCountdraftCount

    • shortcuts.tab1tab9shortcuts.draft1draft9

    • shortcuts.draft10 is deleted (Mod+0 is reserved for display scale reset)

    • mindmap.depthColor* block is dropped (now controlled by color scheme tokens)

  • Color validationcolor is validated as a whole ColorStructure: ramp array lengths (base 5, accent 3), every color string (hex, oklch(...), or transparent), and every RampRef (a literal color string, or a { base | accent: n } index within range / { state: role }) must all be structurally valid, or the entire color field is discarded and re-seeded from defaultColorStructure — there is no per-field fallback the way other settings sections work

  • Frameset validation — validates frameset tree structure; falls back to defaultFramesetTree on invalid input; wipes framesetLeafState on v1→v2 migration

  • Doc 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), and vimrc (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 in validateSettings

  • @takazudo/view-provider — provides FramesetTree type and validateFramesetTree used by validation