feat: user-customizable draft welcome starters
Let users curate the draft welcome chips: pin existing commands and skills as starters, remove them, and drag to reorder — all inline on the draft screen via a '+' picker dialog and per-chip remove, with no separate settings UI. A starter references a command or skill; its scope is inherited from the item (user-scope -> global, project-scope -> per-project). Global starters persist to settings.json (useUIStore + client/server sanitizers); project starters persist to the project config alongside worktree setup commands. The two scopes form ordered namespaces shown global-first then project, reorderable only within each group. The six built-in Session magic-prompt commands are the default global set and stay available in the picker for re-pinning if removed; they keep their bespoke icons, while user commands/skills fall back to the Commands/Skills section icons. Chip labels are normalized (/simplify-code -> 'Simplify code'). Missing commands/skills are skipped rather than shown broken. Drag-to-reorder works on desktop and mobile: rectSortingStrategy for the wrapping multi-row layout, CSS.Translate (no scale) so the lifted chip doesn't stretch, and MouseSensor + long-press TouchSensor so taps still submit and swipes still scroll. The '+' picker is a searchable dialog on every surface.
This commit is contained in:
@@ -4,6 +4,7 @@ import type { SidebarSection } from '@/constants/sidebar';
|
||||
import { getSafeStorage } from './utils/safeStorage';
|
||||
import { SEMANTIC_TYPOGRAPHY, getTypographyVariable, type SemanticTypographyKey } from '@/lib/typography';
|
||||
import type { ShortcutCombo } from '@/lib/shortcuts';
|
||||
import type { DraftStarterRef } from '@/lib/draftStarters';
|
||||
import { DEFAULT_MONO_FONT, DEFAULT_UI_FONT, type MonoFontOption, type UiFontOption } from '@/lib/fontOptions';
|
||||
import { getStoredMobileKeyboardMode, type MobileKeyboardMode } from '@/lib/mobileKeyboardMode';
|
||||
|
||||
@@ -544,6 +545,8 @@ interface UIStore {
|
||||
autoDeleteLastRunAt: number | null;
|
||||
messageLimit: number;
|
||||
fontSize: number;
|
||||
// Global draft welcome starters; null = unset (use the default built-in set).
|
||||
globalDraftStarters: DraftStarterRef[] | null;
|
||||
terminalFontSize: number;
|
||||
uiFont: UiFontOption;
|
||||
monoFont: MonoFontOption;
|
||||
@@ -676,6 +679,7 @@ interface UIStore {
|
||||
setAutoDeleteLastRunAt: (timestamp: number | null) => void;
|
||||
setMessageLimit: (value: number) => void;
|
||||
setFontSize: (size: number) => void;
|
||||
setGlobalDraftStarters: (refs: DraftStarterRef[]) => void;
|
||||
setTerminalFontSize: (size: number) => void;
|
||||
setUiFont: (font: UiFontOption) => void;
|
||||
setMonoFont: (font: MonoFontOption) => void;
|
||||
@@ -811,6 +815,7 @@ export const useUIStore = create<UIStore>()(
|
||||
autoDeleteLastRunAt: null,
|
||||
messageLimit: 200,
|
||||
fontSize: 100,
|
||||
globalDraftStarters: null,
|
||||
terminalFontSize: 13,
|
||||
uiFont: DEFAULT_UI_FONT,
|
||||
monoFont: DEFAULT_MONO_FONT,
|
||||
@@ -1502,6 +1507,10 @@ export const useUIStore = create<UIStore>()(
|
||||
get().applyTypography();
|
||||
},
|
||||
|
||||
setGlobalDraftStarters: (refs) => {
|
||||
set({ globalDraftStarters: refs });
|
||||
},
|
||||
|
||||
setTerminalFontSize: (size) => {
|
||||
const rounded = Math.round(size);
|
||||
const clamped = Math.max(9, Math.min(52, rounded));
|
||||
@@ -2110,6 +2119,7 @@ export const useUIStore = create<UIStore>()(
|
||||
autoDeleteLastRunAt: state.autoDeleteLastRunAt,
|
||||
messageLimit: state.messageLimit,
|
||||
fontSize: state.fontSize,
|
||||
globalDraftStarters: state.globalDraftStarters,
|
||||
terminalFontSize: state.terminalFontSize,
|
||||
uiFont: state.uiFont,
|
||||
monoFont: state.monoFont,
|
||||
|
||||
Reference in New Issue
Block a user