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:
@@ -8,6 +8,7 @@ import { setFilesViewShowGitignored } from '@/lib/filesViewShowGitignored';
|
||||
import { loadAppearancePreferences, applyAppearancePreferences } from '@/lib/appearancePersistence';
|
||||
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||
import { normalizeMobileKeyboardMode, setStoredMobileKeyboardMode } from '@/lib/mobileKeyboardMode';
|
||||
import { sanitizeStarterRefs } from '@/lib/draftStarters';
|
||||
|
||||
const persistToLocalStorage = (settings: DesktopSettings) => {
|
||||
if (typeof window === 'undefined') {
|
||||
@@ -484,6 +485,12 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
|
||||
if (typeof settings.fontSize === 'number' && Number.isFinite(settings.fontSize) && settings.fontSize !== store.fontSize) {
|
||||
store.setFontSize(settings.fontSize);
|
||||
}
|
||||
if (Array.isArray(settings.draftStarters)) {
|
||||
const nextStarters = sanitizeStarterRefs(settings.draftStarters);
|
||||
if (JSON.stringify(store.globalDraftStarters) !== JSON.stringify(nextStarters)) {
|
||||
store.setGlobalDraftStarters(nextStarters);
|
||||
}
|
||||
}
|
||||
if (typeof settings.terminalFontSize === 'number' && Number.isFinite(settings.terminalFontSize) && settings.terminalFontSize !== store.terminalFontSize) {
|
||||
store.setTerminalFontSize(settings.terminalFontSize);
|
||||
}
|
||||
@@ -646,6 +653,9 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
|
||||
)
|
||||
);
|
||||
}
|
||||
if (Array.isArray(candidate.draftStarters)) {
|
||||
result.draftStarters = sanitizeStarterRefs(candidate.draftStarters);
|
||||
}
|
||||
if (typeof candidate.showReasoningTraces === 'boolean') {
|
||||
result.showReasoningTraces = candidate.showReasoningTraces;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user