allozaur/feat/chat slash commands (#26716)

* base : slash-command/misc foundation - model icon and focus-selector constants

* feat : slash-command picker and command parsing helpers

* refactor : wire command and @-mention pickers into the chat form

* ui : improve model selector keyboard navigation and load/dismiss

* feat: Unify markdown/raw-text rendering under one setting with migration

* fix: Misc fixes - tool-call subtitle, assistant wrap, progress guards

* feat: Clamp and style numeric settings inputs from registry bounds
This commit is contained in:
Aleksander Grygier
2026-08-07 20:20:01 +02:00
committed by GitHub
parent f8e30266d2
commit 6de1b63473
41 changed files with 1425 additions and 506 deletions
+31
View File
@@ -0,0 +1,31 @@
/**
* Slash-command token detection for the chat form. Valid only at offset 0.
*/
export function findCommandToken(
value: string
): { name: string; args: string; end: number } | null {
if (!value.startsWith('/')) return null;
const rest = value.slice(1);
const spaceIdx = rest.search(/\s/);
const name = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx);
const args = spaceIdx === -1 ? '' : rest.slice(spaceIdx + 1);
return { name, args, end: value.length };
}
/**
* Stable signature of a slash-command token for use as a "dismissed"
* marker: while the picker is closed and this exact token is still intact,
* the picker does not re-open on in-token edits.
*/
export interface CommandDismissSnapshot {
name: string;
args: string;
}
export function takeCommandDismissSnapshot(value: string): CommandDismissSnapshot | null {
const token = findCommandToken(value);
if (!token) return null;
return { name: token.name, args: token.args };
}
+7
View File
@@ -198,6 +198,13 @@ export {
type MentionDismissSnapshot
} from './mention-token';
// Slash-command token detection (for the `/`-triggered command picker)
export {
findCommandToken,
takeCommandDismissSnapshot,
type CommandDismissSnapshot
} from './command-token';
// Mention-chip visual contract shared by the rehype file-badge plugin,
// plus the `[name](file://...)` link helpers the mention picker splices in
export {
+5 -1
View File
@@ -19,7 +19,9 @@ export function modelLoadStageLabel(stage: ApiModelLoadStage): string {
export function modelLoadFraction(progress: ModelLoadProgress | null): number {
if (!progress) return 0;
const { stages, current, value } = progress;
// The server may emit a progress event before the stage plan is known, so
// `stages` can be absent. Fall back to the raw value in that case.
const { stages = [], current, value } = progress;
const tailCount = Math.max(stages.length - 1, 0);
const textCeiling = 1 - tailCount * MODEL_LOAD_TAIL_SHARE;
const idx = stages.indexOf(current);
@@ -39,5 +41,7 @@ export function modelLoadProgressText(progress: ModelLoadProgress | null): strin
if (!progress) return null;
const label = modelLoadStageLabel(progress.current);
if (!label) return null;
return `${label} ${Math.round(modelLoadFraction(progress) * 100)}%`;
}