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:
@@ -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 };
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)}%`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user