Show Claude icon in terminal header while Claude Code is active (#3046)

This updates the terminal shell-integration badge so it reflects Claude
Code activity instead of always rendering the generic AI icon. When the
active shell command is Claude Code, the header now shows the Claude
logo.

- **Terminal shell-integration badge**
- Updated `getShellIntegrationIconButton()` to render the Claude logo
while Claude Code is the active running command.
- Kept the existing shell-integration states and messaging intact for
non-Claude commands.

- **Claude Code detection**
- Added command detection for Claude Code in the OSC shell-integration
flow.
- Tracks active Claude sessions on `TermWrap`, including initial
runtime-info hydration and command lifecycle transitions.
- Handles common invocation forms, including direct binary paths and
commands wrapped by env var assignments / `env`.

- **UI rendering**
- Added `@lobehub/icons` and used its `Claude` icon in the terminal
header path.
- Reused the existing icon-button rendering contract by passing a React
node for the icon where needed.

- **Focused coverage**
- Added a small unit test for Claude command detection to lock in the
supported command forms.

```ts
const claudeCodeActive = get(this.termRef.current.claudeCodeActiveAtom);
const icon = claudeCodeActive ? React.createElement(TermClaudeIcon) : "sparkles";
```

- **screenshot**
- ![Claude terminal header
badge](https://github.com/user-attachments/assets/4b53f671-8432-4878-b2d2-e3afeba7814f)

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Send tasks to Copilot coding agent from
[Slack](https://gh.io/cca-slack-docs) and
[Teams](https://gh.io/cca-teams-docs) to turn conversations into code.
Copilot posts an update in your thread when it's finished.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
This commit is contained in:
Copilot 2026-03-26 10:59:46 -07:00 committed by GitHub
parent f92a953e07
commit 889e628780
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 159 additions and 62 deletions

View file

@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" fill="#D97757" fill-rule="nonzero"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { isClaudeCodeCommand } from "./osc-handlers";
describe("isClaudeCodeCommand", () => {
it("matches direct Claude Code invocations", () => {
expect(isClaudeCodeCommand("claude")).toBe(true);
expect(isClaudeCodeCommand("claude --dangerously-skip-permissions")).toBe(true);
});
it("matches Claude Code invocations wrapped with env assignments", () => {
expect(isClaudeCodeCommand('ANTHROPIC_API_KEY="test" claude')).toBe(true);
expect(isClaudeCodeCommand("env FOO=bar claude --print")).toBe(true);
});
it("ignores other commands", () => {
expect(isClaudeCodeCommand("claudes")).toBe(false);
expect(isClaudeCodeCommand("echo claude")).toBe(false);
expect(isClaudeCodeCommand("ls ~/claude")).toBe(false);
expect(isClaudeCodeCommand("cat /logs/claude")).toBe(false);
expect(isClaudeCodeCommand("")).toBe(false);
});
});

View file

@ -25,6 +25,8 @@ const Osc52MaxRawLength = 128 * 1024; // includes selector + base64 + whitespace
// See aiprompts/wave-osc-16162.md for full documentation
export type ShellIntegrationStatus = "ready" | "running-command";
const ClaudeCodeRegex = /^claude\b/;
type Osc16162Command =
| { command: "A"; data: Record<string, never> }
| { command: "C"; data: { cmd64?: string } }
@ -43,41 +45,56 @@ type Osc16162Command =
| { command: "I"; data: { inputempty?: boolean } }
| { command: "R"; data: Record<string, never> };
function normalizeCmd(decodedCmd: string): string {
let normalizedCmd = decodedCmd.trim();
normalizedCmd = normalizedCmd.replace(/^env\s+/, "");
normalizedCmd = normalizedCmd.replace(/^(?:\w+=(?:"[^"]*"|'[^']*'|\S+)\s+)*/, "");
return normalizedCmd;
}
function checkCommandForTelemetry(decodedCmd: string) {
if (!decodedCmd) {
return;
}
if (decodedCmd.startsWith("ssh ")) {
const normalizedCmd = normalizeCmd(decodedCmd);
if (normalizedCmd.startsWith("ssh ")) {
recordTEvent("conn:connect", { "conn:conntype": "ssh-manual" });
return;
}
const editorsRegex = /^(vim|vi|nano|nvim)\b/;
if (editorsRegex.test(decodedCmd)) {
if (editorsRegex.test(normalizedCmd)) {
recordTEvent("action:term", { "action:type": "cli-edit" });
return;
}
const tailFollowRegex = /(^|\|\s*)tail\s+-[fF]\b/;
if (tailFollowRegex.test(decodedCmd)) {
if (tailFollowRegex.test(normalizedCmd)) {
recordTEvent("action:term", { "action:type": "cli-tailf" });
return;
}
const claudeRegex = /^claude\b/;
if (claudeRegex.test(decodedCmd)) {
if (ClaudeCodeRegex.test(normalizedCmd)) {
recordTEvent("action:term", { "action:type": "claude" });
return;
}
const opencodeRegex = /^opencode\b/;
if (opencodeRegex.test(decodedCmd)) {
if (opencodeRegex.test(normalizedCmd)) {
recordTEvent("action:term", { "action:type": "opencode" });
return;
}
}
export function isClaudeCodeCommand(decodedCmd: string): boolean {
if (!decodedCmd) {
return false;
}
return ClaudeCodeRegex.test(normalizeCmd(decodedCmd));
}
function handleShellIntegrationCommandStart(
termWrap: TermWrap,
blockId: string,
@ -101,16 +118,20 @@ function handleShellIntegrationCommandStart(
const decodedCmd = base64ToString(cmd.data.cmd64);
rtInfo["shell:lastcmd"] = decodedCmd;
globalStore.set(termWrap.lastCommandAtom, decodedCmd);
const isCC = isClaudeCodeCommand(decodedCmd);
globalStore.set(termWrap.claudeCodeActiveAtom, isCC);
checkCommandForTelemetry(decodedCmd);
} catch (e) {
console.error("Error decoding cmd64:", e);
rtInfo["shell:lastcmd"] = null;
globalStore.set(termWrap.lastCommandAtom, null);
globalStore.set(termWrap.claudeCodeActiveAtom, false);
}
}
} else {
rtInfo["shell:lastcmd"] = null;
globalStore.set(termWrap.lastCommandAtom, null);
globalStore.set(termWrap.claudeCodeActiveAtom, false);
}
rtInfo["shell:lastcmdexitcode"] = null;
}
@ -287,6 +308,7 @@ export function handleOsc16162Command(data: string, blockId: string, loaded: boo
case "A": {
rtInfo["shell:state"] = "ready";
globalStore.set(termWrap.shellIntegrationStatusAtom, "ready");
globalStore.set(termWrap.claudeCodeActiveAtom, false);
const marker = terminal.registerMarker(0);
if (marker) {
termWrap.promptMarkers.push(marker);
@ -324,6 +346,7 @@ export function handleOsc16162Command(data: string, blockId: string, loaded: boo
}
break;
case "D":
globalStore.set(termWrap.claudeCodeActiveAtom, false);
if (cmd.data.exitcode != null) {
rtInfo["shell:lastcmdexitcode"] = cmd.data.exitcode;
} else {
@ -337,6 +360,7 @@ export function handleOsc16162Command(data: string, blockId: string, loaded: boo
break;
case "R":
globalStore.set(termWrap.shellIntegrationStatusAtom, null);
globalStore.set(termWrap.claudeCodeActiveAtom, false);
if (terminal.buffer.active.type === "alternate") {
terminal.write("\x1b[?1049l");
}

View file

@ -10,7 +10,7 @@ import { waveEventSubscribeSingle } from "@/app/store/wps";
import { RpcApi } from "@/app/store/wshclientapi";
import { makeFeBlockRouteId } from "@/app/store/wshrouter";
import { DefaultRouter, TabRpcClient } from "@/app/store/wshrpcutil";
import { TerminalView } from "@/app/view/term/term";
import { TermClaudeIcon, TerminalView } from "@/app/view/term/term";
import { TermWshClient } from "@/app/view/term/term-wsh";
import { VDomModel } from "@/app/view/vdom/vdom-model";
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
@ -404,10 +404,12 @@ export class TermViewModel implements ViewModel {
return null;
}
const shellIntegrationStatus = get(this.termRef.current.shellIntegrationStatusAtom);
const claudeCodeActive = get(this.termRef.current.claudeCodeActiveAtom);
const icon = claudeCodeActive ? React.createElement(TermClaudeIcon) : "sparkles";
if (shellIntegrationStatus == null) {
return {
elemtype: "iconbutton",
icon: "sparkles",
icon,
className: "text-muted",
title: "No shell integration — Wave AI unable to run commands.",
noAction: true,
@ -416,14 +418,16 @@ export class TermViewModel implements ViewModel {
if (shellIntegrationStatus === "ready") {
return {
elemtype: "iconbutton",
icon: "sparkles",
icon,
className: "text-accent",
title: "Shell ready — Wave AI can run commands in this terminal.",
noAction: true,
};
}
if (shellIntegrationStatus === "running-command") {
let title = "Shell busy — Wave AI unable to run commands while another command is running.";
let title = claudeCodeActive
? "Claude Code Detected"
: "Shell busy — Wave AI unable to run commands while another command is running.";
if (this.termRef.current) {
const inAltBuffer = this.termRef.current.terminal?.buffer?.active?.type === "alternate";
@ -436,7 +440,7 @@ export class TermViewModel implements ViewModel {
return {
elemtype: "iconbutton",
icon: "sparkles",
icon,
className: "text-warning",
title: title,
noAction: true,

View file

@ -1,6 +1,7 @@
// Copyright 2025, Command Line Inc.
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import ClaudeColorSvg from "@/app/asset/claude-color.svg";
import { SubBlock } from "@/app/block/block";
import type { BlockNodeModel } from "@/app/block/blocktypes";
import { NullErrorBoundary } from "@/app/element/errorboundary";
@ -34,6 +35,16 @@ interface TerminalViewProps {
model: TermViewModel;
}
const TermClaudeIcon = React.memo(() => {
return (
<div className="[&_svg]:w-[15px] [&_svg]:h-[15px]" aria-hidden="true">
<ClaudeColorSvg />
</div>
);
});
TermClaudeIcon.displayName = "TermClaudeIcon";
const TermResyncHandler = React.memo(({ blockId, model }: TerminalViewProps) => {
const connStatus = jotai.useAtomValue(model.connStatus);
const [lastConnStatus, setLastConnStatus] = React.useState<ConnStatus>(connStatus);
@ -61,7 +72,7 @@ const TermVDomToolbarNode = ({ vdomBlockId, blockId, model }: TerminalViewProps
const unsub = waveEventSubscribeSingle({
eventType: "blockclose",
scope: WOS.makeORef("block", vdomBlockId),
handler: (event) => {
handler: (_event) => {
RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("block", blockId),
meta: {
@ -104,7 +115,7 @@ const TermVDomNodeSingleId = ({ vdomBlockId, blockId, model }: TerminalViewProps
const unsub = waveEventSubscribeSingle({
eventType: "blockclose",
scope: WOS.makeORef("block", vdomBlockId),
handler: (event) => {
handler: (_event) => {
RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("block", blockId),
meta: {
@ -390,4 +401,4 @@ const TerminalView = ({ blockId, model }: ViewComponentProps<TermViewModel>) =>
);
};
export { TerminalView };
export { TermClaudeIcon, TerminalView };

View file

@ -1,4 +1,4 @@
// Copyright 2025, Command Line Inc.
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import type { BlockNodeModel } from "@/app/block/blocktypes";
@ -32,6 +32,7 @@ import {
handleOsc16162Command,
handleOsc52Command,
handleOsc7Command,
isClaudeCodeCommand,
type ShellIntegrationStatus,
} from "./osc-handlers";
import { bufferLinesToText, createTempFileFromBlob, extractAllClipboardData, normalizeCursorStyle } from "./termutil";
@ -92,6 +93,7 @@ export class TermWrap {
promptMarkers: TermTypes.IMarker[] = [];
shellIntegrationStatusAtom: jotai.PrimitiveAtom<ShellIntegrationStatus | null>;
lastCommandAtom: jotai.PrimitiveAtom<string | null>;
claudeCodeActiveAtom: jotai.PrimitiveAtom<boolean>;
nodeModel: BlockNodeModel; // this can be null
hoveredLinkUri: string | null = null;
onLinkHover?: (uri: string | null, mouseX: number, mouseY: number) => void;
@ -131,6 +133,7 @@ export class TermWrap {
this.promptMarkers = [];
this.shellIntegrationStatusAtom = jotai.atom(null) as jotai.PrimitiveAtom<ShellIntegrationStatus | null>;
this.lastCommandAtom = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
this.claudeCodeActiveAtom = jotai.atom(false);
this.webglEnabledAtom = jotai.atom(false) as jotai.PrimitiveAtom<boolean>;
this.terminal = new Terminal(options);
this.fitAddon = new FitAddon();
@ -171,16 +174,34 @@ export class TermWrap {
this.setTermRenderer(WebGLSupported && waveOptions.useWebGl ? "webgl" : "dom");
// Register OSC handlers
this.terminal.parser.registerOscHandler(7, (data: string) => {
return handleOsc7Command(data, this.blockId, this.loaded);
try {
return handleOsc7Command(data, this.blockId, this.loaded);
} catch (e) {
console.error("[termwrap] osc 7 handler error", this.blockId, e);
return false;
}
});
this.terminal.parser.registerOscHandler(52, (data: string) => {
return handleOsc52Command(data, this.blockId, this.loaded, this);
try {
return handleOsc52Command(data, this.blockId, this.loaded, this);
} catch (e) {
console.error("[termwrap] osc 52 handler error", this.blockId, e);
return false;
}
});
this.terminal.parser.registerOscHandler(16162, (data: string) => {
return handleOsc16162Command(data, this.blockId, this.loaded, this);
try {
return handleOsc16162Command(data, this.blockId, this.loaded, this);
} catch (e) {
console.error("[termwrap] osc 16162 handler error", this.blockId, e);
return false;
}
});
this.toDispose.push(
this.terminal.parser.registerCsiHandler({ final: "J" }, (params) => {
if (params == null || params.length < 1) {
return false;
}
if (params[0] === 3) {
this.lastClearScrollbackTs = Date.now();
if (this.inSyncTransaction) {
@ -193,6 +214,9 @@ export class TermWrap {
);
this.toDispose.push(
this.terminal.parser.registerCsiHandler({ prefix: "?", final: "h" }, (params) => {
if (params == null || params.length < 1) {
return false;
}
if (params[0] === 2026) {
this.lastMode2026SetTs = Date.now();
this.inSyncTransaction = true;
@ -202,6 +226,9 @@ export class TermWrap {
);
this.toDispose.push(
this.terminal.parser.registerCsiHandler({ prefix: "?", final: "l" }, (params) => {
if (params == null || params.length < 1) {
return false;
}
if (params[0] === 2026) {
this.lastMode2026ResetTs = Date.now();
this.inSyncTransaction = false;
@ -345,16 +372,19 @@ export class TermWrap {
const rtInfo = await RpcApi.GetRTInfoCommand(TabRpcClient, {
oref: WOS.makeORef("block", this.blockId),
});
let shellState: ShellIntegrationStatus = null;
if (rtInfo && rtInfo["shell:integration"]) {
const shellState = rtInfo["shell:state"] as ShellIntegrationStatus;
shellState = rtInfo["shell:state"] as ShellIntegrationStatus;
globalStore.set(this.shellIntegrationStatusAtom, shellState || null);
} else {
globalStore.set(this.shellIntegrationStatusAtom, null);
}
const lastCmd = rtInfo ? rtInfo["shell:lastcmd"] : null;
const isCC = shellState === "running-command" && isClaudeCodeCommand(lastCmd);
globalStore.set(this.lastCommandAtom, lastCmd || null);
globalStore.set(this.claudeCodeActiveAtom, isCC);
} catch (e) {
console.log("Error loading runtime info:", e);
}
@ -371,7 +401,9 @@ export class TermWrap {
this.promptMarkers.forEach((marker) => {
try {
marker.dispose();
} catch (_) {}
} catch (_) {
/* nothing */
}
});
this.promptMarkers = [];
this.webglContextLossDisposable?.dispose();
@ -380,7 +412,9 @@ export class TermWrap {
this.toDispose.forEach((d) => {
try {
d.dispose();
} catch (_) {}
} catch (_) {
/* nothing */
}
});
this.mainFileSubject.release();
}

80
package-lock.json generated
View file

@ -5434,32 +5434,32 @@
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
"@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
"version": "1.7.6",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
"@floating-ui/core": "^1.7.5",
"@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/react": {
"version": "0.27.16",
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz",
"integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==",
"version": "0.27.19",
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz",
"integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.1.6",
"@floating-ui/utils": "^0.2.10",
"@floating-ui/react-dom": "^2.1.8",
"@floating-ui/utils": "^0.2.11",
"tabbable": "^6.0.0"
},
"peerDependencies": {
@ -5468,12 +5468,12 @@
}
},
"node_modules/@floating-ui/react-dom": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
"integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==",
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz",
"integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==",
"license": "MIT",
"dependencies": {
"@floating-ui/dom": "^1.7.4"
"@floating-ui/dom": "^1.7.6"
},
"peerDependencies": {
"react": ">=16.8.0",
@ -5481,9 +5481,9 @@
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT"
},
"node_modules/@hapi/hoek": {
@ -15182,9 +15182,9 @@
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz",
"integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==",
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
"license": "MIT"
},
"node_modules/emojilib": {
@ -15353,9 +15353,9 @@
}
},
"node_modules/es-toolkit": {
"version": "1.39.10",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz",
"integrity": "sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==",
"version": "1.45.1",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz",
"integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==",
"license": "MIT",
"workspaces": [
"docs",
@ -18948,9 +18948,9 @@
}
},
"node_modules/katex": {
"version": "0.16.22",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz",
"integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==",
"version": "0.16.38",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.38.tgz",
"integrity": "sha512-cjHooZUmIAUmDsHBN+1n8LaZdpmbj03LtYeYPyuYB7OuloiaeaV6N4LcfjcnHVzGWjVQmKrxxTrpDcmSzEZQwQ==",
"funding": [
"https://opencollective.com/katex",
"https://github.com/sponsors/katex"
@ -29918,13 +29918,13 @@
}
},
"node_modules/swr": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
"integrity": "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==",
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.4.1.tgz",
"integrity": "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.3",
"use-sync-external-store": "^1.4.0"
"use-sync-external-store": "^1.6.0"
},
"peerDependencies": {
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@ -29947,9 +29947,9 @@
}
},
"node_modules/tabbable": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
"integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz",
"integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==",
"license": "MIT"
},
"node_modules/tailwind-merge": {
@ -31867,9 +31867,9 @@
}
},
"node_modules/use-sync-external-store": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"