feat(entity): Chunk #4 data layer + entity UI (characters & world-building)
Data layer: - Fuzzy scorer (packages/core/src/fuzzy.ts) with index recovery + unit tests - Backlink computation (packages/core/src/backlinks.ts) incl. entity-level backlinks - Consistency checker (packages/core/src/consistency.ts) incl. reciprocity/scope + unit tests - StorageAdapter contract extended with listEntities/searchEntities/saveEntity/deleteEntity; implemented in Local (Dexie) and Server adapters; contract tests cover entity CRUD + ?q= search - WorldItem.fields now supports list values (keyPeople, rivals); expanded RelationshipKind - @mention autocomplete extension + picker in the editor package Entity UI (apps/web/src/features/entities/): - Generic field renderer (EntityFields) driven by ENTITY_FIELDS/FieldDef - Tabbed directory with fuzzy search + entity cards (EntitiesView) - Create/edit/delete drawer (EntityEditor) reusing shared fields - Relationship editor + force-directed SVG relationship map - Entity engine stylesheet; wired into nav Store: project-store entity methods (load/create/update/delete) over the StorageAdapter contract. CI-green: typecheck, lint, 65 tests, and web build all pass.
This commit is contained in:
@@ -3,24 +3,49 @@ import { applyFontSettings, applyTheme, type ThemeId } from "@pubscript/ui";
|
||||
|
||||
export type Density = "compact" | "comfortable";
|
||||
|
||||
/** Body typeface for the writing column. All are system stacks (no web fonts). */
|
||||
export type FontFamily = "serif" | "sans" | "mono";
|
||||
|
||||
export const FONT_FAMILIES: { value: FontFamily; label: string; css: string }[] = [
|
||||
{ value: "serif", label: "Georgia Serif", css: 'Georgia, "Times New Roman", serif' },
|
||||
{ value: "sans", label: "System Sans", css: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif' },
|
||||
{ value: "mono", label: "Mono", css: '"SFMono-Regular", ui-monospace, "Cascadia Code", Consolas, monospace' },
|
||||
];
|
||||
|
||||
/** Resolve a stored font-family enum to its CSS font-family string. */
|
||||
export function fontFamilyCss(value: FontFamily): string {
|
||||
const found = FONT_FAMILIES.find((f) => f.value === value);
|
||||
return found?.css ?? FONT_FAMILIES.at(0)?.css ?? "";
|
||||
}
|
||||
|
||||
export interface AppSettings {
|
||||
theme: ThemeId;
|
||||
accentColor: string;
|
||||
density: Density;
|
||||
fontFamily: FontFamily;
|
||||
fontSize: number;
|
||||
leading: number;
|
||||
measure: number;
|
||||
showLineNumbers: boolean;
|
||||
wordCountGoal: number;
|
||||
autoSave: boolean;
|
||||
focusMode: boolean;
|
||||
typewriterMode: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: AppSettings = {
|
||||
theme: "ink",
|
||||
accentColor: "#7c8bf5",
|
||||
density: "comfortable",
|
||||
fontFamily: "serif",
|
||||
fontSize: 15,
|
||||
leading: 1.7,
|
||||
measure: 720,
|
||||
showLineNumbers: true,
|
||||
wordCountGoal: 80_000,
|
||||
autoSave: true,
|
||||
focusMode: false,
|
||||
typewriterMode: false,
|
||||
};
|
||||
|
||||
const STORAGE_KEY = "pubscript.settings.v1";
|
||||
@@ -46,14 +71,31 @@ export const useSettings = create<SettingsStore>((set, get) => {
|
||||
const initial = loadSettings();
|
||||
// Apply the persisted theme + font on boot.
|
||||
applyTheme(initial.theme);
|
||||
applyFontSettings({ size: initial.fontSize });
|
||||
applyFontSettings({
|
||||
size: initial.fontSize,
|
||||
leading: initial.leading,
|
||||
measure: initial.measure,
|
||||
family: fontFamilyCss(initial.fontFamily),
|
||||
});
|
||||
|
||||
return {
|
||||
...initial,
|
||||
set: (patch) => {
|
||||
const next = { ...get(), ...patch };
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
||||
if (patch.fontSize != null) applyFontSettings({ size: next.fontSize });
|
||||
if (
|
||||
patch.fontSize != null ||
|
||||
patch.leading != null ||
|
||||
patch.measure != null ||
|
||||
patch.fontFamily != null
|
||||
) {
|
||||
applyFontSettings({
|
||||
size: next.fontSize,
|
||||
leading: next.leading,
|
||||
measure: next.measure,
|
||||
family: fontFamilyCss(next.fontFamily),
|
||||
});
|
||||
}
|
||||
set(next);
|
||||
},
|
||||
setTheme: (theme) => {
|
||||
@@ -63,7 +105,12 @@ export const useSettings = create<SettingsStore>((set, get) => {
|
||||
reset: () => {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
applyTheme(DEFAULT_SETTINGS.theme);
|
||||
applyFontSettings({ size: DEFAULT_SETTINGS.fontSize });
|
||||
applyFontSettings({
|
||||
size: DEFAULT_SETTINGS.fontSize,
|
||||
leading: DEFAULT_SETTINGS.leading,
|
||||
measure: DEFAULT_SETTINGS.measure,
|
||||
family: fontFamilyCss(DEFAULT_SETTINGS.fontFamily),
|
||||
});
|
||||
set({ ...DEFAULT_SETTINGS });
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user