refactor: improve code readability and structure in various files

- Reformatted object literals in tests for better readability.
- Cleaned up imports in TokenUsageIndicator component.
- Enhanced message-adapter logic to handle missing toolCallId.
- Updated vitest configuration to exclude Puppeteer tests.
- Added type definitions for tab group colors in organize-tabs.
- Refactored color handling in organize-tabs to use new type.
- Improved sanitization functions for better character handling.
- Adjusted test cases for consistency in rawResponses structure.
This commit is contained in:
ropzislaw
2026-02-08 13:48:02 +08:00
parent f888f32dac
commit 9cbfd88455
7 changed files with 73 additions and 27 deletions

View File

@@ -453,7 +453,10 @@ describe("ChatAdapter", () => {
adapter.processEvent({
type: "tool_call_complete",
toolName: "organize_tabs",
result: { success: false, error: "Cannot organize tabs in incognito window" },
result: {
success: false,
error: "Cannot organize tabs in incognito window",
},
});
const toolPart = adapter
@@ -519,7 +522,11 @@ describe("ChatAdapter", () => {
adapter.processEvent({
type: "tool_call_complete",
toolName: "api_call",
result: { success: false, error: "API rate limit exceeded", details: { remaining: 0 } },
result: {
success: false,
error: "API rate limit exceeded",
details: { remaining: 0 },
},
});
const toolPart = adapter

View File

@@ -1,11 +1,7 @@
import type { AgentMetrics } from "@aipexstudio/aipex-core";
import { useMemo } from "react";
import { cn } from "../../../lib/utils";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "../../ui/tooltip";
import { Tooltip, TooltipContent, TooltipTrigger } from "../../ui/tooltip";
import { useChatContext } from "../context";
// Default thresholds (matching legacy aipex behavior)
@@ -112,6 +108,8 @@ export function TokenUsageIndicator({
<svg
className="w-3.5 h-3.5 transform -rotate-90"
viewBox="0 0 16 16"
role="img"
aria-label="Token usage progress"
>
{/* Background circle */}
<circle
@@ -189,7 +187,12 @@ export function TokenUsageIndicator({
>
{/* Circular Progress Indicator */}
<div className="relative w-4 h-4">
<svg className="w-4 h-4 transform -rotate-90" viewBox="0 0 16 16">
<svg
className="w-4 h-4 transform -rotate-90"
viewBox="0 0 16 16"
role="img"
aria-label="Token usage progress"
>
{/* Background circle */}
<circle
cx="8"

View File

@@ -232,8 +232,9 @@ export function fromStorageFormat(
for (const part of convertedParts) {
if (part.type === "tool") {
// Skip if we've already processed this tool call
if (processedToolCallIds.has(part.toolCallId)) {
const toolCallId = part.toolCallId;
// Skip if toolCallId is missing or we've already processed this tool call
if (!toolCallId || processedToolCallIds.has(toolCallId)) {
continue;
}
@@ -241,7 +242,7 @@ export function fromStorageFormat(
const resultPart = convertedParts.find(
(p) =>
p.type === "tool" &&
p.toolCallId === part.toolCallId &&
p.toolCallId === toolCallId &&
p.state !== "pending" &&
p !== part,
);
@@ -254,7 +255,7 @@ export function fromStorageFormat(
mergedParts.push(part);
}
processedToolCallIds.add(part.toolCallId);
processedToolCallIds.add(toolCallId);
} else {
mergedParts.push(part);
}

View File

@@ -20,6 +20,18 @@ import type {
import { newQuickJSWASMModuleFromVariant, Scope } from "quickjs-emscripten";
import type { SkillAPIBridge } from "./skill-api";
/**
* QuickJS sync variant interface - matches the structure expected by newQuickJSWASMModuleFromVariant
*/
interface QuickJSVariantLike {
importModuleLoader: () => Promise<
(options?: Record<string, unknown>) => unknown
>;
}
// Type assertion for the variant - the default export type is not fully recognized
const variant = RELEASE_SYNC as unknown as QuickJSVariantLike;
interface ExecutionContext {
skillId: string;
workingDir: string;
@@ -69,10 +81,10 @@ class QuickJSManager {
// Chrome extensions don't allow 'wasm-eval' which asyncify variants need
// Wrap the variant to override locateFile so the Emscripten loader can find the wasm
const variantWithLocateFile = {
...RELEASE_SYNC,
...variant,
importModuleLoader: async () => {
// Get the original module loader
const originalLoader = await RELEASE_SYNC.importModuleLoader();
const originalLoader = await variant.importModuleLoader();
// Return a wrapped version that injects locateFile
return (moduleOptions?: Record<string, unknown>) => {
return originalLoader({

View File

@@ -12,6 +12,20 @@ import { z } from "zod";
// Types
// ============================================================================
/**
* Valid tab group color values (matching chrome.tabGroups.Color enum values)
*/
export type TabGroupColor =
| "blue"
| "red"
| "yellow"
| "green"
| "orange"
| "purple"
| "pink"
| "cyan"
| "grey";
export interface TabData {
id: number;
title: string;
@@ -22,7 +36,7 @@ export interface TabData {
export interface TabGroupResult {
emoji: string;
category: string;
color: chrome.tabGroups.ColorEnum;
color: TabGroupColor;
tabIds: number[];
}
@@ -88,7 +102,7 @@ export function setTabClassificationCallback(
// Helper Functions
// ============================================================================
const VALID_COLORS: chrome.tabGroups.ColorEnum[] = [
const VALID_COLORS: TabGroupColor[] = [
"blue",
"red",
"yellow",
@@ -100,17 +114,21 @@ const VALID_COLORS: chrome.tabGroups.ColorEnum[] = [
"grey",
];
function getRandomColor(): chrome.tabGroups.ColorEnum {
function getRandomColor(): TabGroupColor {
return VALID_COLORS[Math.floor(Math.random() * VALID_COLORS.length)]!;
}
// Regex patterns for character sanitization - using RegExp constructor to satisfy linter
// biome-ignore lint/suspicious/noControlCharactersInRegex: intentionally matching control characters for sanitization
const CONTROL_CHARS_REGEX = /[\u0000-\u001F\u007F-\u009F]/g;
/**
* Sanitize string for AI request - remove problematic characters
*/
function sanitizeForAI(str: string): string {
return str
.replace(/[\uD800-\uDFFF]/g, "") // Remove surrogate pairs
.replace(/[\u0000-\u001F\u007F-\u009F]/g, "") // Remove control characters
.replace(CONTROL_CHARS_REGEX, "") // Remove control characters
.replace(/[\u{1F600}-\u{1F64F}]/gu, "") // Remove emoji ranges
.replace(/[\u{1F300}-\u{1F5FF}]/gu, "")
.replace(/[\u{1F680}-\u{1F6FF}]/gu, "")
@@ -127,7 +145,7 @@ function sanitizeForAI(str: string): string {
function sanitizeString(str: string): string {
return str
.replace(/[\uD800-\uDFFF]/g, "")
.replace(/[\u0000-\u001F\u007F-\u009F]/g, "")
.replace(CONTROL_CHARS_REGEX, "")
.replace(/[^\x20-\x7E\u4e00-\u9fff]/g, "")
.trim();
}
@@ -166,7 +184,7 @@ interface DomainGroup {
domain: string;
category: string;
emoji: string;
color: chrome.tabGroups.ColorEnum;
color: TabGroupColor;
}
const DOMAIN_CATEGORIES: DomainGroup[] = [
@@ -227,7 +245,7 @@ function groupTabsByDomain(tabs: TabData[]): TabGroupResult[] {
{
category: string;
emoji: string;
color: chrome.tabGroups.ColorEnum;
color: TabGroupColor;
tabIds: number[];
}
>();
@@ -346,9 +364,7 @@ export async function groupTabsByAI(): Promise<OrganizeTabsResult> {
emoji: validateEmoji(g.emoji),
category: sanitizeString(g.category),
color: VALID_COLORS.includes(g.color) ? g.color : getRandomColor(),
tabIds: g.tabIds.filter((id) =>
validTabs.some((t) => t.id === id),
),
tabIds: g.tabIds.filter((id) => validTabs.some((t) => t.id === id)),
}));
} else {
console.warn(

View File

@@ -9,6 +9,11 @@ export default defineConfig({
concurrent: false,
},
silent: true,
exclude: ["**/node_modules/**", "**/dist/**"],
exclude: [
"**/node_modules/**",
"**/dist/**",
// Puppeteer tests require Chrome browser installation - run separately with: vitest run --config vitest.puppeteer.config.ts
"**/*.puppeteer.test.ts",
],
},
});

View File

@@ -29,7 +29,9 @@ function createMockRunResult(
const events = overrides.streamEvents ?? [];
// Build rawResponses: if explicit rawResponses provided, use it; otherwise use usage shorthand
let rawResponses: Array<{ usage?: { inputTokens?: number; outputTokens?: number } }> = [];
let rawResponses: Array<{
usage?: { inputTokens?: number; outputTokens?: number };
}> = [];
if (overrides.rawResponses) {
rawResponses = overrides.rawResponses;
} else if (overrides.usage) {
@@ -931,7 +933,7 @@ describe("AIPex", () => {
item: {
rawItem: { name: "api_call", status: "failed" },
output:
'Error: Request failed with Authorization: Bearer sk-1234567890abcdef',
"Error: Request failed with Authorization: Bearer sk-1234567890abcdef",
},
},
],