mirror of
https://github.com/AIPexStudio/AIPex.git
synced 2026-05-13 18:51:35 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user