build(vscode-ide-companion): 添加 SCSS 支持

- 在 esbuild.js 中添加 SCSS 文件处理逻辑
- 在 package.json 中添加 sass 依赖
- 新增代码使用 sass 编译 SCSS 文件,并将其注入到页面中
This commit is contained in:
yiliang114
2025-11-19 23:34:05 +08:00
parent bc2b503e8d
commit 018990b7f6
6 changed files with 595 additions and 13 deletions

407
package-lock.json generated
View File

@@ -2642,6 +2642,354 @@
"node": ">=14"
}
},
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher/node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"peer": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/@parcel/watcher/node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -5824,6 +6172,22 @@
"entities": "^6.0.0"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/chownr": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
@@ -8971,6 +9335,13 @@
"node": ">= 4"
}
},
"node_modules/immutable": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
"integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
"dev": true,
"license": "MIT"
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -12884,6 +13255,20 @@
"node": ">=10"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -13282,6 +13667,27 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/sass": {
"version": "1.94.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.94.1.tgz",
"integrity": "sha512-/YVm5FRQaRlr3oNh2LLFYne1PdPlRZGyKnHh1sLleOqLcohTR4eUUvBjBIqkl1fEXd1MGOHgzJGJh+LgTtV4KQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sax": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
@@ -16320,6 +16726,7 @@
"esbuild": "^0.25.3",
"eslint": "^9.25.1",
"npm-run-all2": "^8.0.2",
"sass": "^1.94.1",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},

View File

@@ -37,6 +37,7 @@ const esbuildProblemMatcherPlugin = {
const cssInjectPlugin = {
name: 'css-inject',
setup(build) {
// Handle CSS files
build.onLoad({ filter: /\.css$/ }, async (args) => {
const fs = await import('fs');
const css = await fs.promises.readFile(args.path, 'utf8');
@@ -49,6 +50,23 @@ const cssInjectPlugin = {
loader: 'js',
};
});
// Handle SCSS files
build.onLoad({ filter: /\.scss$/ }, async (args) => {
const sass = await import('sass');
const result = sass.compile(args.path, {
loadPaths: [args.path.substring(0, args.path.lastIndexOf('/'))],
});
const css = result.css;
return {
contents: `
const style = document.createElement('style');
style.textContent = ${JSON.stringify(css)};
document.head.appendChild(style);
`,
loader: 'js',
};
});
},
};

View File

@@ -178,6 +178,7 @@
"esbuild": "^0.25.3",
"eslint": "^9.25.1",
"npm-run-all2": "^8.0.2",
"sass": "^1.94.1",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},

View File

@@ -298,6 +298,22 @@ button {
padding: var(--app-spacing-medium);
}
.thinking-message {
background-color: var(--app-list-hover-background, rgba(100, 100, 255, 0.1));
opacity: 0.8;
font-style: italic;
position: relative;
padding-left: 24px;
}
.thinking-message::before {
content: "💭";
position: absolute;
left: 6px;
top: 50%;
transform: translateY(-50%);
}
.thinking-label {
font-size: 12px;
font-weight: 600;
@@ -314,6 +330,70 @@ button {
color: rgba(200, 200, 255, 0.9);
}
/* Waiting message styles - similar to Claude Code thinking state */
.message.waiting-message {
opacity: 0.85;
}
.waiting-message .message-content {
background-color: transparent;
border: none;
padding: 8px 0;
display: flex;
align-items: center;
gap: 8px;
}
/* Typing indicator for loading messages */
.typing-indicator {
display: inline-flex;
align-items: center;
margin-right: 0;
gap: 4px;
}
.typing-dot, .thinking-dot {
width: 6px;
height: 6px;
background-color: var(--app-secondary-foreground);
border-radius: 50%;
margin-right: 0;
opacity: 0.6;
animation: typingPulse 1.4s infinite ease-in-out;
}
.typing-dot:nth-child(1),
.thinking-dot:nth-child(1) {
animation-delay: 0s;
}
.typing-dot:nth-child(2),
.thinking-dot:nth-child(2) {
animation-delay: 0.2s;
}
.typing-dot:nth-child(3),
.thinking-dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typingPulse {
0%, 60%, 100% {
transform: scale(0.7);
opacity: 0.6;
}
30% {
transform: scale(1);
opacity: 1;
}
}
.loading-text {
opacity: 0.7;
font-style: italic;
color: var(--app-secondary-foreground);
}
/* ===========================
Scrollbar Styling
=========================== */

View File

@@ -190,13 +190,15 @@ export const App: React.FC = () => {
const [messages, setMessages] = useState<TextMessage[]>([]);
const [inputText, setInputText] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
const [_isWaitingForResponse, setIsWaitingForResponse] = useState(false);
const [_loadingMessage, setLoadingMessage] = useState('');
const [isWaitingForResponse, setIsWaitingForResponse] = useState(false);
const [loadingMessage, setLoadingMessage] = useState('');
const [currentStreamContent, setCurrentStreamContent] = useState('');
const [qwenSessions, setQwenSessions] = useState<
Array<Record<string, unknown>>
>([]);
const [currentSessionId, setCurrentSessionId] = useState<string | null>(null);
const [currentSessionTitle, setCurrentSessionTitle] =
useState<string>('Past Conversations');
const [showSessionSelector, setShowSessionSelector] = useState(false);
const [sessionSearchQuery, setSessionSearchQuery] = useState('');
const [permissionRequest, setPermissionRequest] = useState<{
@@ -336,6 +338,18 @@ export const App: React.FC = () => {
break;
}
case 'thoughtChunk': {
const chunkData = message.data;
// Handle thought chunks for AI thinking display
const thinkingMessage: TextMessage = {
role: 'thinking',
content: chunkData.content || chunkData.chunk || '',
timestamp: Date.now(),
};
setMessages((prev) => [...prev, thinkingMessage]);
break;
}
case 'streamEnd':
// Finalize the streamed message
if (currentStreamContentRef.current) {
@@ -347,12 +361,14 @@ export const App: React.FC = () => {
setMessages((prev) => [...prev, assistantMessage]);
}
setIsStreaming(false);
setIsWaitingForResponse(false); // Clear waiting state
setCurrentStreamContent('');
currentStreamContentRef.current = '';
break;
case 'error':
setIsStreaming(false);
setIsWaitingForResponse(false);
break;
case 'permissionRequest':
@@ -371,10 +387,30 @@ export const App: React.FC = () => {
setQwenSessions(sessions);
// If no current session is selected and there are sessions, select the first one
if (!currentSessionId && sessions.length > 0) {
const firstSession = sessions[0];
const firstSessionId =
(sessions[0].id as string) || (sessions[0].sessionId as string);
(firstSession.id as string) || (firstSession.sessionId as string);
const firstSessionTitle =
(firstSession.title as string) ||
(firstSession.name as string) ||
'Past Conversations';
if (firstSessionId) {
setCurrentSessionId(firstSessionId);
setCurrentSessionTitle(firstSessionTitle);
}
} else if (currentSessionId && sessions.length > 0) {
// Update title for the current session if it exists in the list
const currentSession = sessions.find(
(s: Record<string, unknown>) =>
(s.id as string) === currentSessionId ||
(s.sessionId as string) === currentSessionId,
);
if (currentSession) {
const title =
(currentSession.title as string) ||
(currentSession.name as string) ||
'Past Conversations';
setCurrentSessionTitle(title);
}
}
break;
@@ -391,6 +427,14 @@ export const App: React.FC = () => {
message.data.sessionId,
);
}
// Update current session title
if (message.data.title || message.data.name) {
const title =
(message.data.title as string) ||
(message.data.name as string) ||
'Past Conversations';
setCurrentSessionTitle(title);
}
// Load messages from the session
if (message.data.messages) {
console.log(
@@ -463,6 +507,7 @@ export const App: React.FC = () => {
vscode.postMessage({ type: 'newQwenSession', data: {} });
setShowSessionSelector(false);
setCurrentSessionId(null);
setCurrentSessionTitle('Past Conversations'); // Reset title to default
// Clear messages in UI
setMessages([]);
setCurrentStreamContent('');
@@ -694,7 +739,7 @@ export const App: React.FC = () => {
title="Past conversations"
>
<span className="button-content">
<span className="button-text">Past Conversations</span>
<span className="button-text">{currentSessionTitle}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
@@ -734,14 +779,31 @@ export const App: React.FC = () => {
<EmptyState />
) : (
<>
{messages.map((msg, index) => (
<div key={index} className={`message ${msg.role}`}>
<div className="message-content">{msg.content}</div>
{messages.map((msg, index) => {
// Special styling for thinking messages (Claude Code style)
const messageClass =
msg.role === 'thinking'
? 'message assistant thinking-message'
: `message ${msg.role}`;
return (
<div key={index} className={messageClass}>
<div className="message-content">
{msg.role === 'thinking' && (
<span className="thinking-indicator">
<span className="thinking-dot"></span>
<span className="thinking-dot"></span>
<span className="thinking-dot"></span>
</span>
)}
{msg.content}
</div>
<div className="message-timestamp">
{new Date(msg.timestamp).toLocaleTimeString()}
</div>
</div>
))}
);
})}
{/* Tool Calls */}
{Array.from(toolCalls.values()).map((toolCall) => (
@@ -757,6 +819,20 @@ export const App: React.FC = () => {
/>
)}
{/* Loading/Waiting Message - in message list */}
{isWaitingForResponse && loadingMessage && (
<div className="message assistant waiting-message">
<div className="message-content">
<span className="typing-indicator">
<span className="typing-dot"></span>
<span className="typing-dot"></span>
<span className="typing-dot"></span>
</span>
<span className="loading-text">{loadingMessage}</span>
</div>
</div>
)}
{isStreaming && currentStreamContent && (
<div className="message assistant streaming">
<div className="message-content">{currentStreamContent}</div>
@@ -830,7 +906,7 @@ export const App: React.FC = () => {
role="textbox"
aria-label="Message input"
aria-multiline="true"
data-placeholder="Ask qwen to edit…"
data-placeholder="Ask Qwen Code …"
onInput={(e) => {
const target = e.target as HTMLDivElement;
setInputText(target.textContent || '');

View File

@@ -6,7 +6,7 @@
import ReactDOM from 'react-dom/client';
import { App } from './App.js';
import './App.css';
import './App.scss';
import './ClaudeCodeStyles.css';
const container = document.getElementById('root');