style: auto-fix import/export sorting and formatting
Ran pnpm lint:fix to resolve 220 auto-fixable lint issues. All changes are import/export reordering — no logic changes.
This commit is contained in:
parent
f5d2b7e14f
commit
51f8f3545c
124 changed files with 672 additions and 552 deletions
|
|
@ -16,17 +16,17 @@
|
|||
// On Windows this saturates all threads, blocking the event loop.
|
||||
process.env.UV_THREADPOOL_SIZE ??= '16';
|
||||
|
||||
import { CrossTeamService } from '@main/services/team/CrossTeamService';
|
||||
import { TeamConfigReader } from '@main/services/team/TeamConfigReader';
|
||||
import { TeamInboxWriter } from '@main/services/team/TeamInboxWriter';
|
||||
import { JsonScheduleRepository } from '@main/services/schedule/JsonScheduleRepository';
|
||||
import { ScheduledTaskExecutor } from '@main/services/schedule/ScheduledTaskExecutor';
|
||||
import { SchedulerService } from '@main/services/schedule/SchedulerService';
|
||||
import { ChangeExtractorService } from '@main/services/team/ChangeExtractorService';
|
||||
import { CrossTeamService } from '@main/services/team/CrossTeamService';
|
||||
import { FileContentResolver } from '@main/services/team/FileContentResolver';
|
||||
import { GitDiffFallback } from '@main/services/team/GitDiffFallback';
|
||||
import { ReviewApplierService } from '@main/services/team/ReviewApplierService';
|
||||
import { TeamBackupService } from '@main/services/team/TeamBackupService';
|
||||
import { JsonScheduleRepository } from '@main/services/schedule/JsonScheduleRepository';
|
||||
import { ScheduledTaskExecutor } from '@main/services/schedule/ScheduledTaskExecutor';
|
||||
import { SchedulerService } from '@main/services/schedule/SchedulerService';
|
||||
import { TeamConfigReader } from '@main/services/team/TeamConfigReader';
|
||||
import { TeamInboxWriter } from '@main/services/team/TeamInboxWriter';
|
||||
import {
|
||||
CONTEXT_CHANGED,
|
||||
SCHEDULE_CHANGE,
|
||||
|
|
@ -51,16 +51,32 @@ import { existsSync } from 'fs';
|
|||
import { join } from 'path';
|
||||
|
||||
import { cleanupEditorState, setEditorMainWindow } from './ipc/editor';
|
||||
import { setReviewMainWindow } from './ipc/review';
|
||||
import { initializeIpcHandlers, removeIpcHandlers } from './ipc/handlers';
|
||||
import { setReviewMainWindow } from './ipc/review';
|
||||
import {
|
||||
ApiKeyService,
|
||||
ExtensionFacadeService,
|
||||
GlamaMcpEnrichmentService,
|
||||
McpCatalogAggregator,
|
||||
McpHealthDiagnosticsService,
|
||||
McpInstallationStateService,
|
||||
McpInstallService,
|
||||
OfficialMcpRegistryService,
|
||||
PluginCatalogService,
|
||||
PluginInstallationStateService,
|
||||
PluginInstallService,
|
||||
SkillsCatalogService,
|
||||
SkillsMutationService,
|
||||
SkillsWatcherService,
|
||||
} from './services/extensions';
|
||||
import { startEventLoopLagMonitor } from './services/infrastructure/EventLoopLagMonitor';
|
||||
import { HttpServer } from './services/infrastructure/HttpServer';
|
||||
import { TeamInboxReader } from './services/team/TeamInboxReader';
|
||||
import {
|
||||
buildTeamControlApiBaseUrl,
|
||||
clearTeamControlApiState,
|
||||
writeTeamControlApiState,
|
||||
} from './services/team/TeamControlApiState';
|
||||
import { TeamInboxReader } from './services/team/TeamInboxReader';
|
||||
import { TeamSentMessagesStore } from './services/team/TeamSentMessagesStore';
|
||||
import { getAppIconPath } from './utils/appIcon';
|
||||
import { getProjectsBasePath, getTeamsBasePath, getTodosBasePath } from './utils/pathDecoder';
|
||||
|
|
@ -80,22 +96,6 @@ import {
|
|||
TeamProvisioningService,
|
||||
UpdaterService,
|
||||
} from './services';
|
||||
import {
|
||||
ApiKeyService,
|
||||
ExtensionFacadeService,
|
||||
GlamaMcpEnrichmentService,
|
||||
McpCatalogAggregator,
|
||||
McpHealthDiagnosticsService,
|
||||
McpInstallationStateService,
|
||||
McpInstallService,
|
||||
OfficialMcpRegistryService,
|
||||
PluginCatalogService,
|
||||
PluginInstallationStateService,
|
||||
PluginInstallService,
|
||||
SkillsCatalogService,
|
||||
SkillsMutationService,
|
||||
SkillsWatcherService,
|
||||
} from './services/extensions';
|
||||
|
||||
import type { FileChangeEvent } from '@main/types';
|
||||
import type { TeamChangeEvent } from '@shared/types';
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ import {
|
|||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import { isAgentActionMode } from '../services/team/actionModeInstructions';
|
||||
|
||||
import { validateTaskId, validateTeamName } from './guards';
|
||||
|
||||
import type { CrossTeamService } from '../services/team/CrossTeamService';
|
||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||
import type { IpcResult, TaskRef } from '@shared/types';
|
||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||
|
||||
const logger = createLogger('IPC:crossTeam');
|
||||
|
||||
|
|
|
|||
|
|
@ -6,30 +6,6 @@
|
|||
* Phase 5: install/uninstall mutations.
|
||||
*/
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import type {
|
||||
ApiKeyEntry,
|
||||
ApiKeyLookupResult,
|
||||
ApiKeySaveRequest,
|
||||
ApiKeyStorageStatus,
|
||||
EnrichedPlugin,
|
||||
InstalledMcpEntry,
|
||||
McpCatalogItem,
|
||||
McpCustomInstallRequest,
|
||||
McpInstallRequest,
|
||||
McpServerDiagnostic,
|
||||
McpSearchResult,
|
||||
OperationResult,
|
||||
PluginInstallRequest,
|
||||
} from '@shared/types/extensions';
|
||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||
|
||||
import type { ExtensionFacadeService } from '../services/extensions/ExtensionFacadeService';
|
||||
import type { PluginInstallService } from '../services/extensions/install/PluginInstallService';
|
||||
import type { McpInstallService } from '../services/extensions/install/McpInstallService';
|
||||
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
||||
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
||||
|
||||
import {
|
||||
API_KEYS_DELETE,
|
||||
API_KEYS_LIST,
|
||||
|
|
@ -50,9 +26,32 @@ import {
|
|||
PLUGIN_INSTALL,
|
||||
PLUGIN_UNINSTALL,
|
||||
} from '@preload/constants/ipcChannels';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import { GitHubStarsService } from '../services/extensions/catalog/GitHubStarsService';
|
||||
|
||||
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
||||
import type { ExtensionFacadeService } from '../services/extensions/ExtensionFacadeService';
|
||||
import type { McpInstallService } from '../services/extensions/install/McpInstallService';
|
||||
import type { PluginInstallService } from '../services/extensions/install/PluginInstallService';
|
||||
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
||||
import type {
|
||||
ApiKeyEntry,
|
||||
ApiKeyLookupResult,
|
||||
ApiKeySaveRequest,
|
||||
ApiKeyStorageStatus,
|
||||
EnrichedPlugin,
|
||||
InstalledMcpEntry,
|
||||
McpCatalogItem,
|
||||
McpCustomInstallRequest,
|
||||
McpInstallRequest,
|
||||
McpSearchResult,
|
||||
McpServerDiagnostic,
|
||||
OperationResult,
|
||||
PluginInstallRequest,
|
||||
} from '@shared/types/extensions';
|
||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||
|
||||
const logger = createLogger('IPC:extensions');
|
||||
|
||||
/** Allowed scope values */
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ import {
|
|||
registerSessionHandlers,
|
||||
removeSessionHandlers,
|
||||
} from './sessions';
|
||||
import { initializeSshHandlers, registerSshHandlers, removeSshHandlers } from './ssh';
|
||||
import { initializeSkillsHandlers, registerSkillsHandlers, removeSkillsHandlers } from './skills';
|
||||
import { initializeSshHandlers, registerSshHandlers, removeSshHandlers } from './ssh';
|
||||
import {
|
||||
initializeSubagentHandlers,
|
||||
registerSubagentHandlers,
|
||||
|
|
@ -103,18 +103,18 @@ import type {
|
|||
TeamProvisioningService,
|
||||
UpdaterService,
|
||||
} from '../services';
|
||||
import type { HttpServer } from '../services/infrastructure/HttpServer';
|
||||
import type { CrossTeamService } from '../services/team/CrossTeamService';
|
||||
import type { TeamBackupService } from '../services/team/TeamBackupService';
|
||||
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
||||
import type { ExtensionFacadeService } from '../services/extensions/ExtensionFacadeService';
|
||||
import type { McpInstallService } from '../services/extensions/install/McpInstallService';
|
||||
import type { PluginInstallService } from '../services/extensions/install/PluginInstallService';
|
||||
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
||||
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
||||
import type { SkillsCatalogService } from '../services/extensions/skills/SkillsCatalogService';
|
||||
import type { SkillsMutationService } from '../services/extensions/skills/SkillsMutationService';
|
||||
import type { SkillsWatcherService } from '../services/extensions/skills/SkillsWatcherService';
|
||||
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
||||
import type { HttpServer } from '../services/infrastructure/HttpServer';
|
||||
import type { SchedulerService } from '../services/schedule/SchedulerService';
|
||||
import type { CrossTeamService } from '../services/team/CrossTeamService';
|
||||
import type { TeamBackupService } from '../services/team/TeamBackupService';
|
||||
|
||||
/**
|
||||
* Initializes IPC handlers with service registry.
|
||||
|
|
|
|||
|
|
@ -1,18 +1,3 @@
|
|||
import { createLogger } from '@shared/utils/logger';
|
||||
import type {
|
||||
SkillCatalogItem,
|
||||
SkillDeleteRequest,
|
||||
SkillDetail,
|
||||
SkillImportRequest,
|
||||
SkillReviewPreview,
|
||||
SkillUpsertRequest,
|
||||
} from '@shared/types/extensions';
|
||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||
|
||||
import type { SkillsCatalogService } from '../services/extensions/skills/SkillsCatalogService';
|
||||
import type { SkillsMutationService } from '../services/extensions/skills/SkillsMutationService';
|
||||
import type { SkillsWatcherService } from '../services/extensions/skills/SkillsWatcherService';
|
||||
|
||||
import {
|
||||
SKILLS_APPLY_IMPORT,
|
||||
SKILLS_APPLY_UPSERT,
|
||||
|
|
@ -24,6 +9,20 @@ import {
|
|||
SKILLS_START_WATCHING,
|
||||
SKILLS_STOP_WATCHING,
|
||||
} from '@preload/constants/ipcChannels';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type { SkillsCatalogService } from '../services/extensions/skills/SkillsCatalogService';
|
||||
import type { SkillsMutationService } from '../services/extensions/skills/SkillsMutationService';
|
||||
import type { SkillsWatcherService } from '../services/extensions/skills/SkillsWatcherService';
|
||||
import type {
|
||||
SkillCatalogItem,
|
||||
SkillDeleteRequest,
|
||||
SkillDetail,
|
||||
SkillImportRequest,
|
||||
SkillReviewPreview,
|
||||
SkillUpsertRequest,
|
||||
} from '@shared/types/extensions';
|
||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||
|
||||
const logger = createLogger('IPC:skills');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import crypto from 'crypto';
|
||||
|
||||
import { setCurrentMainOp } from '@main/services/infrastructure/EventLoopLagMonitor';
|
||||
import { getAppIconPath } from '@main/utils/appIcon';
|
||||
import { getAppDataPath } from '@main/utils/pathDecoder';
|
||||
|
|
@ -29,8 +27,8 @@ import {
|
|||
TEAM_LAUNCH,
|
||||
TEAM_LEAD_ACTIVITY,
|
||||
TEAM_LEAD_CONTEXT,
|
||||
TEAM_MEMBER_SPAWN_STATUSES,
|
||||
TEAM_LIST,
|
||||
TEAM_MEMBER_SPAWN_STATUSES,
|
||||
TEAM_PERMANENTLY_DELETE,
|
||||
TEAM_PREPARE_PROVISIONING,
|
||||
TEAM_PROCESS_ALIVE,
|
||||
|
|
@ -72,17 +70,18 @@ import {
|
|||
} from '@shared/utils/cliArgsParser';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { isRateLimitMessage } from '@shared/utils/rateLimitDetector';
|
||||
import crypto from 'crypto';
|
||||
import { BrowserWindow, type IpcMain, type IpcMainInvokeEvent, Notification } from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { ConfigManager } from '../services/infrastructure/ConfigManager';
|
||||
import { NotificationManager } from '../services/infrastructure/NotificationManager';
|
||||
import { gitIdentityResolver } from '../services/parsing/GitIdentityResolver';
|
||||
import {
|
||||
buildActionModeAgentBlock,
|
||||
isAgentActionMode,
|
||||
} from '../services/team/actionModeInstructions';
|
||||
import { gitIdentityResolver } from '../services/parsing/GitIdentityResolver';
|
||||
import { TeamAttachmentStore } from '../services/team/TeamAttachmentStore';
|
||||
import { buildAddMemberSpawnMessage } from '../services/team/TeamProvisioningService';
|
||||
import { TeamTaskAttachmentStore } from '../services/team/TeamTaskAttachmentStore';
|
||||
|
|
@ -113,13 +112,13 @@ import type {
|
|||
GlobalTask,
|
||||
IpcResult,
|
||||
KanbanColumnId,
|
||||
LeadContextUsage,
|
||||
LeadActivitySnapshot,
|
||||
LeadContextUsage,
|
||||
LeadContextUsageSnapshot,
|
||||
MemberFullStats,
|
||||
MemberSpawnStatusesSnapshot,
|
||||
MemberLogSummary,
|
||||
MemberSpawnStatusEntry,
|
||||
MemberSpawnStatusesSnapshot,
|
||||
SendMessageRequest,
|
||||
SendMessageResult,
|
||||
TaskAttachmentMeta,
|
||||
|
|
@ -548,7 +547,10 @@ async function handlePermanentlyDeleteTeam(
|
|||
.rm(path.join(appData, 'attachments', validated.value!), { recursive: true, force: true })
|
||||
.catch(() => undefined);
|
||||
await fs.promises
|
||||
.rm(path.join(appData, 'task-attachments', validated.value!), { recursive: true, force: true })
|
||||
.rm(path.join(appData, 'task-attachments', validated.value!), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
})
|
||||
.catch(() => undefined);
|
||||
// Mark in backup registry AFTER successful deletion
|
||||
if (teamBackupService) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
*/
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import { type McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
||||
import { type PluginCatalogService } from './catalog/PluginCatalogService';
|
||||
import { type McpInstallationStateService } from './state/McpInstallationStateService';
|
||||
import { type PluginInstallationStateService } from './state/PluginInstallationStateService';
|
||||
|
||||
import type {
|
||||
EnrichedPlugin,
|
||||
InstalledMcpEntry,
|
||||
|
|
@ -15,11 +21,6 @@ import type {
|
|||
PluginCatalogItem,
|
||||
} from '@shared/types/extensions';
|
||||
|
||||
import { PluginCatalogService } from './catalog/PluginCatalogService';
|
||||
import { McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
||||
import { PluginInstallationStateService } from './state/PluginInstallationStateService';
|
||||
import { McpInstallationStateService } from './state/McpInstallationStateService';
|
||||
|
||||
const logger = createLogger('Extensions:Facade');
|
||||
|
||||
export class ExtensionFacadeService {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@
|
|||
* Storage file: ~/.claude/api-keys.json
|
||||
*/
|
||||
|
||||
import { safeStorage } from 'electron';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { safeStorage } from 'electron';
|
||||
|
||||
import type {
|
||||
ApiKeyEntry,
|
||||
ApiKeyLookupResult,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
import https from 'node:https';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { parseGitHubOwnerRepo } from '@shared/utils/extensionNormalizers';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
const logger = createLogger('Extensions:GitHubStars');
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ export class GitHubStarsService {
|
|||
*/
|
||||
async fetchStars(repositoryUrls: string[]): Promise<Record<string, number>> {
|
||||
const result: Record<string, number> = {};
|
||||
const tasks: Array<{ url: string; owner: string; repo: string }> = [];
|
||||
const tasks: { url: string; owner: string; repo: string }[] = [];
|
||||
|
||||
for (const url of repositoryUrls) {
|
||||
const parsed = parseGitHubOwnerRepo(url);
|
||||
|
|
@ -107,10 +107,10 @@ export class GitHubStarsService {
|
|||
* Run async tasks with a concurrency limit.
|
||||
*/
|
||||
private async withConcurrencyLimit(
|
||||
tasks: Array<() => Promise<void>>,
|
||||
tasks: (() => Promise<void>)[],
|
||||
limit: number
|
||||
): Promise<Array<'ok' | 'error'>> {
|
||||
const results: Array<'ok' | 'error'> = [];
|
||||
): Promise<('ok' | 'error')[]> {
|
||||
const results: ('ok' | 'error')[] = [];
|
||||
let index = 0;
|
||||
|
||||
const run = async (): Promise<void> => {
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@
|
|||
* Cursor-based pagination (after), no auth required.
|
||||
*/
|
||||
|
||||
import https from 'node:https';
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type { McpCatalogItem, McpHostingType, McpToolDef } from '@shared/types/extensions';
|
||||
|
||||
const logger = createLogger('Extensions:GlamaMcp');
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@
|
|||
* - Provides getById() for secure install flow
|
||||
*/
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import type { McpCatalogItem, McpSearchResult } from '@shared/types/extensions';
|
||||
import { normalizeRepoUrl } from '@shared/utils/extensionNormalizers';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import { OfficialMcpRegistryService } from './OfficialMcpRegistryService';
|
||||
import { GlamaMcpEnrichmentService } from './GlamaMcpEnrichmentService';
|
||||
import { type GlamaMcpEnrichmentService } from './GlamaMcpEnrichmentService';
|
||||
import { type OfficialMcpRegistryService } from './OfficialMcpRegistryService';
|
||||
|
||||
import type { McpCatalogItem, McpSearchResult } from '@shared/types/extensions';
|
||||
|
||||
const logger = createLogger('Extensions:McpAggregator');
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@
|
|||
* Filters for _meta.isLatest to pick only latest versions.
|
||||
*/
|
||||
|
||||
import https from 'node:https';
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type {
|
||||
McpAuthHeaderDef,
|
||||
McpCatalogItem,
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@
|
|||
* - Deduplicates concurrent requests
|
||||
*/
|
||||
|
||||
import https from 'node:https';
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import type { PluginCatalogItem } from '@shared/types/extensions';
|
||||
import { buildPluginId } from '@shared/utils/extensionNormalizers';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type { PluginCatalogItem } from '@shared/types/extensions';
|
||||
|
||||
const logger = createLogger('Extensions:PluginCatalog');
|
||||
|
||||
|
|
@ -260,7 +261,7 @@ export class PluginCatalogService {
|
|||
|
||||
const json = JSON.parse(response.body) as MarketplaceJson;
|
||||
const items = this.parseMarketplace(json);
|
||||
const etag = (response.headers['etag'] as string) ?? null;
|
||||
const etag = (response.headers.etag as string) ?? null;
|
||||
|
||||
this.cache = { items, etag, fetchedAt: Date.now() };
|
||||
logger.info(`Fetched ${items.length} plugins from marketplace "${json.name}"`);
|
||||
|
|
@ -311,7 +312,7 @@ export class PluginCatalogService {
|
|||
* e.g. https://github.com/org/repo → https://raw.githubusercontent.com/org/repo/main/README.md
|
||||
*/
|
||||
private buildReadmeUrl(repoUrl: string): string | null {
|
||||
const match = repoUrl.match(/github\.com\/([^/]+)\/([^/]+)/);
|
||||
const match = /github\.com\/([^/]+)\/([^/]+)/.exec(repoUrl);
|
||||
if (!match) return null;
|
||||
const [, owner, repo] = match;
|
||||
return `https://raw.githubusercontent.com/${owner}/${repo}/main/README.md`;
|
||||
|
|
|
|||
|
|
@ -2,26 +2,26 @@
|
|||
* Extension services barrel export.
|
||||
*/
|
||||
|
||||
export { PluginCatalogService } from './catalog/PluginCatalogService';
|
||||
export { OfficialMcpRegistryService } from './catalog/OfficialMcpRegistryService';
|
||||
export { GlamaMcpEnrichmentService } from './catalog/GlamaMcpEnrichmentService';
|
||||
export { McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
||||
export { PluginInstallationStateService } from './state/PluginInstallationStateService';
|
||||
export { McpInstallationStateService } from './state/McpInstallationStateService';
|
||||
export { McpHealthDiagnosticsService } from './state/McpHealthDiagnosticsService';
|
||||
export { ExtensionFacadeService } from './ExtensionFacadeService';
|
||||
export { PluginInstallService } from './install/PluginInstallService';
|
||||
export { McpInstallService } from './install/McpInstallService';
|
||||
export { ApiKeyService } from './apikeys/ApiKeyService';
|
||||
export { GitHubStarsService } from './catalog/GitHubStarsService';
|
||||
export { SkillRootsResolver } from './skills/SkillRootsResolver';
|
||||
export { SkillScanner } from './skills/SkillScanner';
|
||||
export { SkillMetadataParser } from './skills/SkillMetadataParser';
|
||||
export { SkillValidator } from './skills/SkillValidator';
|
||||
export { SkillsCatalogService } from './skills/SkillsCatalogService';
|
||||
export { SkillScaffoldService } from './skills/SkillScaffoldService';
|
||||
export { GlamaMcpEnrichmentService } from './catalog/GlamaMcpEnrichmentService';
|
||||
export { McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
||||
export { OfficialMcpRegistryService } from './catalog/OfficialMcpRegistryService';
|
||||
export { PluginCatalogService } from './catalog/PluginCatalogService';
|
||||
export { ExtensionFacadeService } from './ExtensionFacadeService';
|
||||
export { McpInstallService } from './install/McpInstallService';
|
||||
export { PluginInstallService } from './install/PluginInstallService';
|
||||
export { SkillImportService } from './skills/SkillImportService';
|
||||
export { SkillMetadataParser } from './skills/SkillMetadataParser';
|
||||
export { SkillPlanService } from './skills/SkillPlanService';
|
||||
export { SkillReviewService } from './skills/SkillReviewService';
|
||||
export { SkillRootsResolver } from './skills/SkillRootsResolver';
|
||||
export { SkillScaffoldService } from './skills/SkillScaffoldService';
|
||||
export { SkillScanner } from './skills/SkillScanner';
|
||||
export { SkillsCatalogService } from './skills/SkillsCatalogService';
|
||||
export { SkillsMutationService } from './skills/SkillsMutationService';
|
||||
export { SkillsWatcherService } from './skills/SkillsWatcherService';
|
||||
export { SkillValidator } from './skills/SkillValidator';
|
||||
export { McpHealthDiagnosticsService } from './state/McpHealthDiagnosticsService';
|
||||
export { McpInstallationStateService } from './state/McpInstallationStateService';
|
||||
export { PluginInstallationStateService } from './state/PluginInstallationStateService';
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
import { execCli } from '@main/utils/childProcess';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type { McpCatalogAggregator } from '../catalog/McpCatalogAggregator';
|
||||
import type {
|
||||
McpCustomInstallRequest,
|
||||
McpInstallRequest,
|
||||
OperationResult,
|
||||
} from '@shared/types/extensions';
|
||||
import type { McpCatalogAggregator } from '../catalog/McpCatalogAggregator';
|
||||
|
||||
const logger = createLogger('Extensions:McpInstall');
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
import { execCli } from '@main/utils/childProcess';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type { OperationResult, PluginInstallRequest } from '@shared/types/extensions';
|
||||
import type { PluginCatalogService } from '../catalog/PluginCatalogService';
|
||||
import type { OperationResult, PluginInstallRequest } from '@shared/types/extensions';
|
||||
|
||||
const logger = createLogger('Extensions:PluginInstall');
|
||||
|
||||
|
|
|
|||
|
|
@ -101,13 +101,11 @@ export class SkillImportService {
|
|||
}
|
||||
}
|
||||
|
||||
private async walkDirectory(
|
||||
rootDir: string
|
||||
): Promise<{
|
||||
files: Array<{ absolutePath: string; relativePath: string }>;
|
||||
private async walkDirectory(rootDir: string): Promise<{
|
||||
files: { absolutePath: string; relativePath: string }[];
|
||||
hiddenEntriesSkipped: number;
|
||||
}> {
|
||||
const allFiles: Array<{ absolutePath: string; relativePath: string }> = [];
|
||||
const allFiles: { absolutePath: string; relativePath: string }[] = [];
|
||||
let hiddenEntriesSkipped = 0;
|
||||
let totalBytes = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import * as path from 'node:path';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import YAML from 'yaml';
|
||||
|
||||
import type { ResolvedSkillRoot } from './SkillRootsResolver';
|
||||
import type {
|
||||
SkillCatalogItem,
|
||||
SkillDetail,
|
||||
|
|
@ -8,9 +11,6 @@ import type {
|
|||
SkillInvocationMode,
|
||||
SkillValidationIssue,
|
||||
} from '@shared/types/extensions';
|
||||
import YAML from 'yaml';
|
||||
|
||||
import type { ResolvedSkillRoot } from './SkillRootsResolver';
|
||||
|
||||
const logger = createLogger('Extensions:SkillParser');
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ export class SkillMetadataParser {
|
|||
};
|
||||
}
|
||||
|
||||
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/u);
|
||||
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/u.exec(content);
|
||||
if (!match) {
|
||||
return {
|
||||
rawFrontmatter: null,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { createHash } from 'node:crypto';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
import { SkillScanner } from './SkillScanner';
|
||||
|
||||
import type { ImportedSkillSourceFile } from './SkillImportService';
|
||||
import type {
|
||||
SkillDraftFile,
|
||||
SkillReviewFileChange,
|
||||
|
|
@ -10,10 +13,6 @@ import type {
|
|||
SkillReviewSummary,
|
||||
} from '@shared/types/extensions';
|
||||
|
||||
import type { ImportedSkillSourceFile } from './SkillImportService';
|
||||
|
||||
import { SkillScanner } from './SkillScanner';
|
||||
|
||||
type SkillPlanInputFile =
|
||||
| { relativePath: string; isBinary: false; content: string }
|
||||
| { relativePath: string; isBinary: true; sourceAbsolutePath: string };
|
||||
|
|
@ -73,7 +72,7 @@ export class SkillPlanService {
|
|||
async applyPlan(plan: SkillExecutionPlan): Promise<void> {
|
||||
const backupRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'skill-plan-backup-'));
|
||||
const createdPaths: string[] = [];
|
||||
const backups: Array<{ absolutePath: string; backupPath: string }> = [];
|
||||
const backups: { absolutePath: string; backupPath: string }[] = [];
|
||||
|
||||
try {
|
||||
for (const [index, change] of plan.changes.entries()) {
|
||||
|
|
@ -200,7 +199,7 @@ export class SkillPlanService {
|
|||
|
||||
const summary = changes.reduce<SkillReviewSummary>(
|
||||
(acc, change) => {
|
||||
acc[`${change.action}d` as 'created' | 'updated' | 'deleted'] += 1;
|
||||
acc[`${change.action}d`] += 1;
|
||||
if (change.isBinary) {
|
||||
acc.binary += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import * as fs from 'node:fs/promises';
|
|||
import * as path from 'node:path';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import type { SkillDraftFile, SkillReviewFileChange } from '@shared/types/extensions';
|
||||
|
||||
import type { ImportedSkillSourceFile } from './SkillImportService';
|
||||
import type { SkillDraftFile, SkillReviewFileChange } from '@shared/types/extensions';
|
||||
|
||||
const logger = createLogger('Extensions:SkillReview');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import * as path from 'node:path';
|
||||
|
||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
|
||||
import type { SkillRootKind, SkillScope } from '@shared/types/extensions';
|
||||
|
||||
export interface ResolvedSkillRoot {
|
||||
|
|
@ -10,7 +11,7 @@ export interface ResolvedSkillRoot {
|
|||
rootPath: string;
|
||||
}
|
||||
|
||||
const USER_ROOTS: Array<{ rootKind: SkillRootKind; segments: string[] }> = [
|
||||
const USER_ROOTS: { rootKind: SkillRootKind; segments: string[] }[] = [
|
||||
{ rootKind: 'claude', segments: ['.claude', 'skills'] },
|
||||
{ rootKind: 'cursor', segments: ['.cursor', 'skills'] },
|
||||
{ rootKind: 'agents', segments: ['.agents', 'skills'] },
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import * as fs from 'node:fs/promises';
|
|||
import * as path from 'node:path';
|
||||
|
||||
import { isPathWithinRoot, validateFileName } from '@main/utils/pathValidation';
|
||||
import type { SkillDraftFile, SkillRootKind, SkillScope } from '@shared/types/extensions';
|
||||
|
||||
import { SkillRootsResolver } from './SkillRootsResolver';
|
||||
|
||||
import type { SkillDraftFile, SkillRootKind, SkillScope } from '@shared/types/extensions';
|
||||
|
||||
export class SkillScaffoldService {
|
||||
constructor(private readonly rootsResolver = new SkillRootsResolver()) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import type { SkillCatalogItem, SkillDirectoryFlags } from '@shared/types/extensions';
|
||||
|
||||
import { SkillMetadataParser, type SkillRelatedFiles } from './SkillMetadataParser';
|
||||
|
||||
import type { ResolvedSkillRoot } from './SkillRootsResolver';
|
||||
import type { SkillCatalogItem, SkillDirectoryFlags } from '@shared/types/extensions';
|
||||
|
||||
const SKILL_FILE_CANDIDATES = ['SKILL.md', 'Skill.md', 'skill.md'] as const;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@ import * as fs from 'node:fs/promises';
|
|||
import * as path from 'node:path';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
||||
|
||||
import { SkillMetadataParser } from './SkillMetadataParser';
|
||||
import { SkillRootsResolver, type ResolvedSkillRoot } from './SkillRootsResolver';
|
||||
import { type ResolvedSkillRoot, SkillRootsResolver } from './SkillRootsResolver';
|
||||
import { SkillScanner } from './SkillScanner';
|
||||
import { SkillValidator } from './SkillValidator';
|
||||
|
||||
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
||||
|
||||
const logger = createLogger('Extensions:SkillsCatalog');
|
||||
|
||||
export class SkillsCatalogService {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import { isPathWithinRoot, validateFileName } from '@main/utils/pathValidation';
|
||||
import { shell } from 'electron';
|
||||
|
||||
import { SkillImportService } from './SkillImportService';
|
||||
import { SkillPlanService } from './SkillPlanService';
|
||||
import { SkillRootsResolver } from './SkillRootsResolver';
|
||||
import { SkillScaffoldService } from './SkillScaffoldService';
|
||||
import { SkillsCatalogService } from './SkillsCatalogService';
|
||||
|
||||
import type {
|
||||
SkillDeleteRequest,
|
||||
SkillDetail,
|
||||
|
|
@ -8,15 +17,6 @@ import type {
|
|||
SkillReviewPreview,
|
||||
SkillUpsertRequest,
|
||||
} from '@shared/types/extensions';
|
||||
import { shell } from 'electron';
|
||||
|
||||
import { isPathWithinRoot, validateFileName } from '@main/utils/pathValidation';
|
||||
|
||||
import { SkillImportService } from './SkillImportService';
|
||||
import { SkillPlanService } from './SkillPlanService';
|
||||
import { SkillScaffoldService } from './SkillScaffoldService';
|
||||
import { SkillRootsResolver } from './SkillRootsResolver';
|
||||
import { SkillsCatalogService } from './SkillsCatalogService';
|
||||
|
||||
export class SkillsMutationService {
|
||||
constructor(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { createLogger } from '@shared/utils/logger';
|
||||
import type { SkillWatcherEvent } from '@shared/types/extensions';
|
||||
import { isPathWithinRoot } from '@main/utils/pathValidation';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { watch } from 'chokidar';
|
||||
|
||||
import { SkillRootsResolver } from './SkillRootsResolver';
|
||||
|
||||
import type { SkillWatcherEvent } from '@shared/types/extensions';
|
||||
import type { FSWatcher } from 'chokidar';
|
||||
|
||||
const logger = createLogger('Extensions:SkillsWatcher');
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@
|
|||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import type { InstalledMcpEntry } from '@shared/types/extensions';
|
||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type { InstalledMcpEntry } from '@shared/types/extensions';
|
||||
|
||||
const logger = createLogger('Extensions:McpState');
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@
|
|||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import { getClaudeBasePath } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import type { InstalledPluginEntry } from '@shared/types/extensions';
|
||||
import type { InstallScope } from '@shared/types/extensions';
|
||||
import { getClaudeBasePath } from '@main/utils/pathDecoder';
|
||||
|
||||
const logger = createLogger('Extensions:PluginState');
|
||||
|
||||
|
|
@ -29,24 +30,24 @@ interface InstalledPluginsJson {
|
|||
version: number;
|
||||
plugins: Record<
|
||||
string, // qualifiedName
|
||||
Array<{
|
||||
{
|
||||
scope: string;
|
||||
installPath?: string;
|
||||
version?: string;
|
||||
installedAt?: string;
|
||||
lastUpdated?: string;
|
||||
gitCommitSha?: string;
|
||||
}>
|
||||
}[]
|
||||
>;
|
||||
}
|
||||
|
||||
interface InstallCountsJson {
|
||||
version: number;
|
||||
fetchedAt: string;
|
||||
counts: Array<{
|
||||
counts: {
|
||||
plugin: string; // qualifiedName format
|
||||
unique_installs: number;
|
||||
}>;
|
||||
}[];
|
||||
}
|
||||
|
||||
// ── Cache ──────────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@
|
|||
export * from './analysis';
|
||||
export * from './discovery';
|
||||
export * from './error';
|
||||
export * from './extensions';
|
||||
export * from './infrastructure';
|
||||
export * from './parsing';
|
||||
export * from './team';
|
||||
export * from './schedule';
|
||||
export * from './extensions';
|
||||
export * from './team';
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import { createLogger } from '@shared/utils/logger';
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import type { Schedule, ScheduleRun } from '@shared/types';
|
||||
import type { ScheduleRepository } from './ScheduleRepository';
|
||||
import type { Schedule, ScheduleRun } from '@shared/types';
|
||||
|
||||
const logger = createLogger('Service:JsonScheduleRepo');
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function extractSummaryFromStreamJson(stdout: string): string {
|
|||
|
||||
const content = (parsed.content ??
|
||||
(parsed.message as Record<string, unknown> | undefined)?.content) as
|
||||
| Array<{ type?: string; text?: string }>
|
||||
| { type?: string; text?: string }[]
|
||||
| undefined;
|
||||
if (!Array.isArray(content)) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import { createLogger } from '@shared/utils/logger';
|
|||
import { Cron } from 'croner';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
import type { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
||||
import type { ScheduleRepository } from './ScheduleRepository';
|
||||
import type {
|
||||
CreateScheduleInput,
|
||||
Schedule,
|
||||
|
|
@ -18,8 +20,6 @@ import type {
|
|||
ScheduleRunStatus,
|
||||
UpdateSchedulePatch,
|
||||
} from '@shared/types';
|
||||
import type { ScheduleRepository } from './ScheduleRepository';
|
||||
import type { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
||||
|
||||
const logger = createLogger('Service:Scheduler');
|
||||
|
||||
|
|
@ -511,7 +511,7 @@ export class SchedulerService {
|
|||
|
||||
private async onCronTick(scheduleId: string): Promise<void> {
|
||||
const schedule = await this.repository.getSchedule(scheduleId);
|
||||
if (!schedule || schedule.status !== 'active') {
|
||||
if (schedule?.status !== 'active') {
|
||||
logger.debug(`Cron tick for ${scheduleId} skipped (not active)`);
|
||||
return;
|
||||
}
|
||||
|
|
@ -659,7 +659,7 @@ export class SchedulerService {
|
|||
}
|
||||
|
||||
const freshSchedule = await this.repository.getSchedule(schedule.id);
|
||||
if (!freshSchedule || freshSchedule.status !== 'active') {
|
||||
if (freshSchedule?.status !== 'active') {
|
||||
await this.completeRun(retryRun, 'failed', exitCode, undefined, error);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
*/
|
||||
|
||||
export { JsonScheduleRepository } from './JsonScheduleRepository';
|
||||
export type { ScheduleRepository } from './ScheduleRepository';
|
||||
export { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
||||
export type {
|
||||
ExecutionRequest,
|
||||
InternalScheduleRun,
|
||||
ScheduledTaskResult,
|
||||
} from './ScheduledTaskExecutor';
|
||||
export { SchedulerService } from './SchedulerService';
|
||||
export { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
||||
export type { ScheduleRepository } from './ScheduleRepository';
|
||||
export type { WarmUpFn } from './SchedulerService';
|
||||
export { SchedulerService } from './SchedulerService';
|
||||
|
|
|
|||
|
|
@ -577,7 +577,7 @@ export class ChangeExtractorService {
|
|||
|
||||
private async parseJSONLFilesWithConcurrency(
|
||||
paths: string[]
|
||||
): Promise<Array<{ snippets: SnippetDiff[]; mtime: number }>> {
|
||||
): Promise<{ snippets: SnippetDiff[]; mtime: number }[]> {
|
||||
if (paths.length === 0) return [];
|
||||
|
||||
const results = new Array<{ snippets: SnippetDiff[]; mtime: number }>(paths.length);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { CROSS_TEAM_SENT_SOURCE, CROSS_TEAM_SOURCE, formatCrossTeamText } from '@shared/constants';
|
||||
import { getClaudeBasePath, getTeamsBasePath } from '@main/utils/pathDecoder';
|
||||
import { CROSS_TEAM_SENT_SOURCE, CROSS_TEAM_SOURCE, formatCrossTeamText } from '@shared/constants';
|
||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import * as agentTeamsControllerModule from 'agent-teams-controller';
|
||||
|
|
|
|||
|
|
@ -300,7 +300,8 @@ export class TeamBackupService {
|
|||
if (raw && isValidConfig(raw)) {
|
||||
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||
manifest.displayName = typeof parsed.name === 'string' ? parsed.name : undefined;
|
||||
manifest.projectPath = typeof parsed.projectPath === 'string' ? parsed.projectPath : undefined;
|
||||
manifest.projectPath =
|
||||
typeof parsed.projectPath === 'string' ? parsed.projectPath : undefined;
|
||||
}
|
||||
} catch {
|
||||
// best-effort
|
||||
|
|
@ -392,7 +393,7 @@ export class TeamBackupService {
|
|||
}
|
||||
|
||||
const cached = manifest.fileStats[descriptor.relPath];
|
||||
if (cached && cached.mtime === stat.mtimeMs && cached.size === stat.size) {
|
||||
if (cached?.mtime === stat.mtimeMs && cached.size === stat.size) {
|
||||
return false; // not dirty
|
||||
}
|
||||
|
||||
|
|
@ -431,7 +432,7 @@ export class TeamBackupService {
|
|||
if (stat.size > MAX_FILE_SIZE_BYTES) return; // skip oversized silently during shutdown
|
||||
|
||||
const cached = manifest.fileStats[descriptor.relPath];
|
||||
if (cached && cached.mtime === stat.mtimeMs && cached.size === stat.size) return;
|
||||
if (cached?.mtime === stat.mtimeMs && cached.size === stat.size) return;
|
||||
|
||||
const destPath = path.join(backupDir, descriptor.relPath);
|
||||
|
||||
|
|
@ -572,10 +573,7 @@ export class TeamBackupService {
|
|||
return count > 0;
|
||||
}
|
||||
|
||||
private async restoreGenericPartial(
|
||||
teamName: string,
|
||||
manifest: BackupManifest
|
||||
): Promise<number> {
|
||||
private async restoreGenericPartial(teamName: string, manifest: BackupManifest): Promise<number> {
|
||||
const backupDir = this.getBackupDir(teamName);
|
||||
const backupFiles = await this.enumerateBackupFiles(teamName);
|
||||
let count = 0;
|
||||
|
|
@ -892,10 +890,20 @@ export class TeamBackupService {
|
|||
return path.join(getTasksBasePath(), teamName, relPath.slice('tasks/'.length));
|
||||
}
|
||||
if (relPath.startsWith('attachments/')) {
|
||||
return path.join(getAppDataPath(), 'attachments', teamName, relPath.slice('attachments/'.length));
|
||||
return path.join(
|
||||
getAppDataPath(),
|
||||
'attachments',
|
||||
teamName,
|
||||
relPath.slice('attachments/'.length)
|
||||
);
|
||||
}
|
||||
if (relPath.startsWith('task-attachments/')) {
|
||||
return path.join(getAppDataPath(), 'task-attachments', teamName, relPath.slice('task-attachments/'.length));
|
||||
return path.join(
|
||||
getAppDataPath(),
|
||||
'task-attachments',
|
||||
teamName,
|
||||
relPath.slice('task-attachments/'.length)
|
||||
);
|
||||
}
|
||||
return path.join(getTeamsBasePath(), teamName, relPath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { execFile } from 'child_process';
|
||||
import { randomUUID } from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
|
||||
import { atomicWriteAsync } from './atomicWrite';
|
||||
|
||||
interface McpLaunchSpec {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
/* eslint-disable no-param-reassign -- ProvisioningRun object is intentionally mutated as a state tracker throughout the provisioning lifecycle */
|
||||
import { ConfigManager } from '@main/services/infrastructure/ConfigManager';
|
||||
import { killProcessTree, spawnCli } from '@main/utils/childProcess';
|
||||
import { shouldAutoAllow } from '@main/utils/toolApprovalRules';
|
||||
import { FileReadTimeoutError, readFileUtf8WithTimeout } from '@main/utils/fsRead';
|
||||
import { resolveInteractiveShellEnv } from '@main/utils/shellEnv';
|
||||
import {
|
||||
encodePath,
|
||||
extractBaseDir,
|
||||
|
|
@ -14,6 +12,8 @@ import {
|
|||
getTasksBasePath,
|
||||
getTeamsBasePath,
|
||||
} from '@main/utils/pathDecoder';
|
||||
import { resolveInteractiveShellEnv } from '@main/utils/shellEnv';
|
||||
import { shouldAutoAllow } from '@main/utils/toolApprovalRules';
|
||||
import {
|
||||
AGENT_BLOCK_CLOSE,
|
||||
AGENT_BLOCK_OPEN,
|
||||
|
|
@ -21,8 +21,8 @@ import {
|
|||
} from '@shared/constants/agentBlocks';
|
||||
import {
|
||||
CROSS_TEAM_PREFIX_TAG,
|
||||
CROSS_TEAM_SOURCE,
|
||||
CROSS_TEAM_SENT_SOURCE,
|
||||
CROSS_TEAM_SOURCE,
|
||||
parseCrossTeamPrefix,
|
||||
stripCrossTeamPrefix,
|
||||
} from '@shared/constants/crossTeam';
|
||||
|
|
@ -41,14 +41,14 @@ import {
|
|||
import { createCliAutoSuffixNameGuard, parseNumericSuffixName } from '@shared/utils/teamMemberName';
|
||||
import { extractToolPreview, formatToolSummaryFromCalls } from '@shared/utils/toolSummary';
|
||||
import * as agentTeamsControllerModule from 'agent-teams-controller';
|
||||
import { spawn, type ChildProcess } from 'child_process';
|
||||
import { type ChildProcess, type spawn } from 'child_process';
|
||||
import { randomUUID } from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import { atomicWriteAsync } from './atomicWrite';
|
||||
import { buildActionModeProtocol } from './actionModeInstructions';
|
||||
import { atomicWriteAsync } from './atomicWrite';
|
||||
import { ClaudeBinaryResolver } from './ClaudeBinaryResolver';
|
||||
import { withFileLock } from './fileLock';
|
||||
import { withInboxLock } from './inboxLock';
|
||||
|
|
@ -231,10 +231,10 @@ interface ProvisioningRun {
|
|||
rejectOnce: (error: string) => void;
|
||||
timeoutHandle: NodeJS.Timeout;
|
||||
} | null;
|
||||
activeCrossTeamReplyHints: Array<{
|
||||
activeCrossTeamReplyHints: {
|
||||
toTeam: string;
|
||||
conversationId: string;
|
||||
}>;
|
||||
}[];
|
||||
/** Monotonic counter for individual lead assistant messages. */
|
||||
leadMsgSeq: number;
|
||||
/** Accumulated tool_use details between text messages. */
|
||||
|
|
@ -1122,11 +1122,11 @@ interface CachedProbeResult {
|
|||
cachedAtMs: number;
|
||||
}
|
||||
|
||||
type ProbeResult = {
|
||||
interface ProbeResult {
|
||||
claudePath: string;
|
||||
authSource: ProvisioningAuthSource;
|
||||
warning?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type AuthWarningSource = 'probe' | 'stdout' | 'stderr' | 'assistant' | 'pre-complete';
|
||||
|
||||
|
|
@ -1607,21 +1607,21 @@ export class TeamProvisioningService {
|
|||
private async matchCrossTeamLeadInboxMessages(
|
||||
teamName: string,
|
||||
leadName: string,
|
||||
deliveredBlocks: Array<{
|
||||
deliveredBlocks: {
|
||||
teammateId: string;
|
||||
content: string;
|
||||
toTeam: string;
|
||||
conversationId: string;
|
||||
}>
|
||||
}[]
|
||||
): Promise<
|
||||
Array<{
|
||||
{
|
||||
teammateId: string;
|
||||
content: string;
|
||||
toTeam: string;
|
||||
conversationId: string;
|
||||
messageId: string;
|
||||
wasRead: boolean;
|
||||
}>
|
||||
}[]
|
||||
> {
|
||||
if (deliveredBlocks.length === 0) return [];
|
||||
|
||||
|
|
@ -1633,14 +1633,14 @@ export class TeamProvisioningService {
|
|||
}
|
||||
|
||||
const usedMessageIds = new Set<string>();
|
||||
const matches: Array<{
|
||||
const matches: {
|
||||
teammateId: string;
|
||||
content: string;
|
||||
toTeam: string;
|
||||
conversationId: string;
|
||||
messageId: string;
|
||||
wasRead: boolean;
|
||||
}> = [];
|
||||
}[] = [];
|
||||
for (const block of deliveredBlocks) {
|
||||
const matchesBlock = (message: InboxMessage, requireExactText: boolean): boolean => {
|
||||
if (message.source !== CROSS_TEAM_SOURCE) return false;
|
||||
|
|
@ -1816,7 +1816,7 @@ export class TeamProvisioningService {
|
|||
private rememberPendingInboxRelayCandidates(
|
||||
run: ProvisioningRun,
|
||||
recipient: string,
|
||||
messages: Array<Pick<InboxMessage, 'messageId' | 'text' | 'summary'>>
|
||||
messages: Pick<InboxMessage, 'messageId' | 'text' | 'summary'>[]
|
||||
): string[] {
|
||||
const candidates = this.prunePendingInboxRelayCandidates(run);
|
||||
const queuedAtMs = Date.now();
|
||||
|
|
@ -4167,7 +4167,7 @@ export class TeamProvisioningService {
|
|||
(mistakenToolHint ? { teamName: mistakenToolHint.toTeam, memberName: 'team-lead' } : null);
|
||||
if (crossTeamRecipient && this.crossTeamSender) {
|
||||
const inferredReplyMeta =
|
||||
mistakenToolHint && mistakenToolHint.toTeam === crossTeamRecipient.teamName
|
||||
mistakenToolHint?.toTeam === crossTeamRecipient.teamName
|
||||
? {
|
||||
conversationId: mistakenToolHint.conversationId,
|
||||
replyToConversationId: mistakenToolHint.conversationId,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getTaskChangeSummariesBasePath } from '@main/utils/pathDecoder';
|
||||
import { atomicWriteAsync } from '@main/utils/atomicWrite';
|
||||
import { getTaskChangeSummariesBasePath } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { TASK_CHANGE_SUMMARY_CACHE_SCHEMA_VERSION } from './taskChangeSummaryCacheTypes';
|
||||
|
||||
import type { FileChangeSummary, TaskChangeSetV2 } from '@shared/types';
|
||||
import type { PersistedTaskChangeSummaryEntry } from './taskChangeSummaryCacheTypes';
|
||||
import type { FileChangeSummary, TaskChangeSetV2 } from '@shared/types';
|
||||
|
||||
function normalizeIsoString(value: unknown): string | null {
|
||||
if (typeof value !== 'string' || value.trim() === '') return null;
|
||||
|
|
|
|||
|
|
@ -707,8 +707,8 @@ async function readTasksDirForTeam(
|
|||
? (parsed.attachments as unknown[])
|
||||
: undefined,
|
||||
sourceMessageId:
|
||||
typeof parsed.sourceMessageId === 'string' && (parsed.sourceMessageId as string).trim()
|
||||
? (parsed.sourceMessageId as string).trim()
|
||||
typeof parsed.sourceMessageId === 'string' && parsed.sourceMessageId.trim()
|
||||
? parsed.sourceMessageId.trim()
|
||||
: undefined,
|
||||
sourceMessage:
|
||||
parsed.sourceMessage &&
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@ import { WINDOW_ZOOM_FACTOR_CHANGED_CHANNEL } from '@shared/constants';
|
|||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
import {
|
||||
API_KEYS_DELETE,
|
||||
API_KEYS_LIST,
|
||||
API_KEYS_LOOKUP,
|
||||
API_KEYS_SAVE,
|
||||
API_KEYS_STORAGE_STATUS,
|
||||
APP_RELAUNCH,
|
||||
CLI_INSTALLER_GET_STATUS,
|
||||
CLI_INSTALLER_INSTALL,
|
||||
|
|
@ -34,28 +39,30 @@ import {
|
|||
HTTP_SERVER_GET_STATUS,
|
||||
HTTP_SERVER_START,
|
||||
HTTP_SERVER_STOP,
|
||||
MCP_GITHUB_STARS,
|
||||
MCP_REGISTRY_BROWSE,
|
||||
MCP_REGISTRY_DIAGNOSE,
|
||||
MCP_REGISTRY_GET_BY_ID,
|
||||
MCP_REGISTRY_GET_INSTALLED,
|
||||
MCP_REGISTRY_INSTALL,
|
||||
MCP_REGISTRY_INSTALL_CUSTOM,
|
||||
MCP_REGISTRY_SEARCH,
|
||||
MCP_REGISTRY_UNINSTALL,
|
||||
PLUGIN_GET_ALL,
|
||||
PLUGIN_GET_README,
|
||||
PLUGIN_INSTALL,
|
||||
PLUGIN_UNINSTALL,
|
||||
PROJECT_LIST_FILES,
|
||||
RENDERER_BOOT,
|
||||
RENDERER_HEARTBEAT,
|
||||
RENDERER_LOG,
|
||||
SCHEDULE_CHANGE,
|
||||
SCHEDULE_CREATE,
|
||||
SCHEDULE_DELETE,
|
||||
SCHEDULE_GET,
|
||||
SCHEDULE_GET_RUN_LOGS,
|
||||
SCHEDULE_GET_RUNS,
|
||||
SCHEDULE_LIST,
|
||||
SCHEDULE_PAUSE,
|
||||
SCHEDULE_RESUME,
|
||||
SCHEDULE_TRIGGER_NOW,
|
||||
SCHEDULE_UPDATE,
|
||||
REVIEW_APPLY_DECISIONS,
|
||||
REVIEW_CHECK_CONFLICT,
|
||||
REVIEW_CLEAR_DECISIONS,
|
||||
REVIEW_FILE_CHANGE,
|
||||
REVIEW_GET_AGENT_CHANGES,
|
||||
REVIEW_GET_CHANGE_STATS,
|
||||
REVIEW_GET_FILE_CONTENT,
|
||||
REVIEW_FILE_CHANGE,
|
||||
REVIEW_GET_GIT_FILE_LOG,
|
||||
REVIEW_GET_TASK_CHANGES,
|
||||
REVIEW_INVALIDATE_TASK_CHANGE_SUMMARIES,
|
||||
|
|
@ -67,6 +74,27 @@ import {
|
|||
REVIEW_SAVE_EDITED_FILE,
|
||||
REVIEW_UNWATCH_FILES,
|
||||
REVIEW_WATCH_FILES,
|
||||
SCHEDULE_CHANGE,
|
||||
SCHEDULE_CREATE,
|
||||
SCHEDULE_DELETE,
|
||||
SCHEDULE_GET,
|
||||
SCHEDULE_GET_RUN_LOGS,
|
||||
SCHEDULE_GET_RUNS,
|
||||
SCHEDULE_LIST,
|
||||
SCHEDULE_PAUSE,
|
||||
SCHEDULE_RESUME,
|
||||
SCHEDULE_TRIGGER_NOW,
|
||||
SCHEDULE_UPDATE,
|
||||
SKILLS_APPLY_IMPORT,
|
||||
SKILLS_APPLY_UPSERT,
|
||||
SKILLS_CHANGED,
|
||||
SKILLS_DELETE,
|
||||
SKILLS_GET_DETAIL,
|
||||
SKILLS_LIST,
|
||||
SKILLS_PREVIEW_IMPORT,
|
||||
SKILLS_PREVIEW_UPSERT,
|
||||
SKILLS_START_WATCHING,
|
||||
SKILLS_STOP_WATCHING,
|
||||
SSH_CONNECT,
|
||||
SSH_DISCONNECT,
|
||||
SSH_GET_CONFIG_HOSTS,
|
||||
|
|
@ -101,8 +129,8 @@ import {
|
|||
TEAM_LAUNCH,
|
||||
TEAM_LEAD_ACTIVITY,
|
||||
TEAM_LEAD_CONTEXT,
|
||||
TEAM_MEMBER_SPAWN_STATUSES,
|
||||
TEAM_LIST,
|
||||
TEAM_MEMBER_SPAWN_STATUSES,
|
||||
TEAM_PERMANENTLY_DELETE,
|
||||
TEAM_PREPARE_PROVISIONING,
|
||||
TEAM_PROCESS_ALIVE,
|
||||
|
|
@ -149,34 +177,6 @@ import {
|
|||
WINDOW_IS_MAXIMIZED,
|
||||
WINDOW_MAXIMIZE,
|
||||
WINDOW_MINIMIZE,
|
||||
PLUGIN_GET_ALL,
|
||||
PLUGIN_GET_README,
|
||||
PLUGIN_INSTALL,
|
||||
PLUGIN_UNINSTALL,
|
||||
MCP_REGISTRY_SEARCH,
|
||||
MCP_REGISTRY_BROWSE,
|
||||
MCP_REGISTRY_DIAGNOSE,
|
||||
MCP_REGISTRY_GET_BY_ID,
|
||||
MCP_REGISTRY_GET_INSTALLED,
|
||||
MCP_REGISTRY_INSTALL,
|
||||
MCP_REGISTRY_INSTALL_CUSTOM,
|
||||
MCP_REGISTRY_UNINSTALL,
|
||||
MCP_GITHUB_STARS,
|
||||
SKILLS_APPLY_IMPORT,
|
||||
SKILLS_APPLY_UPSERT,
|
||||
SKILLS_CHANGED,
|
||||
SKILLS_DELETE,
|
||||
SKILLS_GET_DETAIL,
|
||||
SKILLS_LIST,
|
||||
SKILLS_PREVIEW_IMPORT,
|
||||
SKILLS_PREVIEW_UPSERT,
|
||||
SKILLS_START_WATCHING,
|
||||
SKILLS_STOP_WATCHING,
|
||||
API_KEYS_LIST,
|
||||
API_KEYS_SAVE,
|
||||
API_KEYS_DELETE,
|
||||
API_KEYS_LOOKUP,
|
||||
API_KEYS_STORAGE_STATUS,
|
||||
} from './constants/ipcChannels';
|
||||
import {
|
||||
CONFIG_ADD_CUSTOM_PROJECT_PATH,
|
||||
|
|
@ -236,9 +236,9 @@ import type {
|
|||
KanbanColumnId,
|
||||
LeadActivitySnapshot,
|
||||
LeadContextUsageSnapshot,
|
||||
MemberSpawnStatusesSnapshot,
|
||||
MemberFullStats,
|
||||
MemberLogSummary,
|
||||
MemberSpawnStatusesSnapshot,
|
||||
NotificationTrigger,
|
||||
RejectResult,
|
||||
ReplaceMembersRequest,
|
||||
|
|
@ -281,28 +281,6 @@ import type {
|
|||
UpdateSchedulePatch,
|
||||
WslClaudeRootCandidate,
|
||||
} from '@shared/types';
|
||||
import type {
|
||||
ApiKeyEntry,
|
||||
ApiKeyLookupResult,
|
||||
ApiKeySaveRequest,
|
||||
ApiKeyStorageStatus,
|
||||
EnrichedPlugin,
|
||||
InstalledMcpEntry,
|
||||
McpCatalogItem,
|
||||
McpCustomInstallRequest,
|
||||
McpInstallRequest,
|
||||
McpServerDiagnostic,
|
||||
McpSearchResult,
|
||||
OperationResult,
|
||||
PluginInstallRequest,
|
||||
SkillCatalogItem,
|
||||
SkillDeleteRequest,
|
||||
SkillDetail,
|
||||
SkillImportRequest,
|
||||
SkillReviewPreview,
|
||||
SkillUpsertRequest,
|
||||
SkillWatcherEvent,
|
||||
} from '@shared/types/extensions';
|
||||
import type {
|
||||
BinaryPreviewResult,
|
||||
CreateDirResponse,
|
||||
|
|
@ -318,6 +296,28 @@ import type {
|
|||
SearchInFilesResult,
|
||||
WriteFileResponse,
|
||||
} from '@shared/types/editor';
|
||||
import type {
|
||||
ApiKeyEntry,
|
||||
ApiKeyLookupResult,
|
||||
ApiKeySaveRequest,
|
||||
ApiKeyStorageStatus,
|
||||
EnrichedPlugin,
|
||||
InstalledMcpEntry,
|
||||
McpCatalogItem,
|
||||
McpCustomInstallRequest,
|
||||
McpInstallRequest,
|
||||
McpSearchResult,
|
||||
McpServerDiagnostic,
|
||||
OperationResult,
|
||||
PluginInstallRequest,
|
||||
SkillCatalogItem,
|
||||
SkillDeleteRequest,
|
||||
SkillDetail,
|
||||
SkillImportRequest,
|
||||
SkillReviewPreview,
|
||||
SkillUpsertRequest,
|
||||
SkillWatcherEvent,
|
||||
} from '@shared/types/extensions';
|
||||
import type { PtySpawnOptions } from '@shared/types/terminal';
|
||||
import type { CliArgsValidationResult } from '@shared/utils/cliArgsParser';
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import {
|
|||
import { getTeamColorSet, getThemedBadge } from '@renderer/constants/teamColors';
|
||||
import { useTheme } from '@renderer/hooks/useTheme';
|
||||
import { useStore } from '@renderer/store';
|
||||
import type { SearchMatch } from '@renderer/store/types';
|
||||
import { REHYPE_PLUGINS, REHYPE_PLUGINS_NO_HIGHLIGHT } from '@renderer/utils/markdownPlugins';
|
||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||
import { parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
||||
|
|
@ -44,6 +43,8 @@ import {
|
|||
import { FileLink, isRelativeUrl } from './FileLink';
|
||||
import { MermaidDiagram } from './MermaidDiagram';
|
||||
|
||||
import type { SearchMatch } from '@renderer/store/types';
|
||||
|
||||
// =============================================================================
|
||||
// Types
|
||||
// =============================================================================
|
||||
|
|
@ -69,7 +70,7 @@ interface MarkdownViewerProps {
|
|||
onTeamClick?: (teamName: string) => void;
|
||||
}
|
||||
|
||||
const EMPTY_TEAMS: Array<{ teamName?: string; displayName?: string; color?: string }> = [];
|
||||
const EMPTY_TEAMS: { teamName?: string; displayName?: string; color?: string }[] = [];
|
||||
const EMPTY_TEAM_COLOR_MAP = new Map<string, string>();
|
||||
const EMPTY_SEARCH_MATCHES: SearchMatch[] = [];
|
||||
const NOOP_TEAM_CLICK = (): void => undefined;
|
||||
|
|
@ -94,31 +95,136 @@ function allowCustomProtocols(url: string): string {
|
|||
* that appear in agent messages and cause React "unrecognized tag" warnings.
|
||||
*/
|
||||
const STANDARD_HTML_TAGS = new Set([
|
||||
'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio',
|
||||
'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button',
|
||||
'canvas', 'caption', 'cite', 'code', 'col', 'colgroup',
|
||||
'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt',
|
||||
'em', 'embed',
|
||||
'fieldset', 'figcaption', 'figure', 'footer', 'form',
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html',
|
||||
'i', 'iframe', 'img', 'input', 'ins',
|
||||
'a',
|
||||
'abbr',
|
||||
'address',
|
||||
'area',
|
||||
'article',
|
||||
'aside',
|
||||
'audio',
|
||||
'b',
|
||||
'base',
|
||||
'bdi',
|
||||
'bdo',
|
||||
'blockquote',
|
||||
'body',
|
||||
'br',
|
||||
'button',
|
||||
'canvas',
|
||||
'caption',
|
||||
'cite',
|
||||
'code',
|
||||
'col',
|
||||
'colgroup',
|
||||
'data',
|
||||
'datalist',
|
||||
'dd',
|
||||
'del',
|
||||
'details',
|
||||
'dfn',
|
||||
'dialog',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'em',
|
||||
'embed',
|
||||
'fieldset',
|
||||
'figcaption',
|
||||
'figure',
|
||||
'footer',
|
||||
'form',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'head',
|
||||
'header',
|
||||
'hgroup',
|
||||
'hr',
|
||||
'html',
|
||||
'i',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'ins',
|
||||
'kbd',
|
||||
'label', 'legend', 'li', 'link',
|
||||
'main', 'map', 'mark', 'menu', 'meta', 'meter',
|
||||
'nav', 'noscript',
|
||||
'object', 'ol', 'optgroup', 'option', 'output',
|
||||
'p', 'picture', 'pre', 'progress',
|
||||
'label',
|
||||
'legend',
|
||||
'li',
|
||||
'link',
|
||||
'main',
|
||||
'map',
|
||||
'mark',
|
||||
'menu',
|
||||
'meta',
|
||||
'meter',
|
||||
'nav',
|
||||
'noscript',
|
||||
'object',
|
||||
'ol',
|
||||
'optgroup',
|
||||
'option',
|
||||
'output',
|
||||
'p',
|
||||
'picture',
|
||||
'pre',
|
||||
'progress',
|
||||
'q',
|
||||
'rp', 'rt', 'ruby',
|
||||
's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'source', 'span',
|
||||
'strong', 'style', 'sub', 'summary', 'sup',
|
||||
'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track',
|
||||
'u', 'ul',
|
||||
'var', 'video',
|
||||
'rp',
|
||||
'rt',
|
||||
'ruby',
|
||||
's',
|
||||
'samp',
|
||||
'script',
|
||||
'search',
|
||||
'section',
|
||||
'select',
|
||||
'slot',
|
||||
'small',
|
||||
'source',
|
||||
'span',
|
||||
'strong',
|
||||
'style',
|
||||
'sub',
|
||||
'summary',
|
||||
'sup',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'template',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'time',
|
||||
'title',
|
||||
'tr',
|
||||
'track',
|
||||
'u',
|
||||
'ul',
|
||||
'var',
|
||||
'video',
|
||||
'wbr',
|
||||
// SVG elements commonly used inline
|
||||
'svg', 'path', 'circle', 'rect', 'line', 'polyline', 'polygon', 'g', 'defs', 'use',
|
||||
'text', 'tspan', 'clippath', 'mask', 'pattern', 'image', 'foreignobject',
|
||||
'svg',
|
||||
'path',
|
||||
'circle',
|
||||
'rect',
|
||||
'line',
|
||||
'polyline',
|
||||
'polygon',
|
||||
'g',
|
||||
'defs',
|
||||
'use',
|
||||
'text',
|
||||
'tspan',
|
||||
'clippath',
|
||||
'mask',
|
||||
'pattern',
|
||||
'image',
|
||||
'foreignobject',
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import React, { Component, type ErrorInfo, type ReactNode } from 'react';
|
||||
|
||||
import { useStore } from '@renderer/store';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { AlertTriangle, Bug, Check, Copy, RefreshCw } from 'lucide-react';
|
||||
|
||||
import {
|
||||
type BugReportContext,
|
||||
buildBugReportText,
|
||||
buildGitHubBugReportUrl,
|
||||
type BugReportContext,
|
||||
} from '@renderer/utils/bugReportUtils';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { AlertTriangle, Bug, Check, Copy, RefreshCw } from 'lucide-react';
|
||||
|
||||
const logger = createLogger('Component:ErrorBoundary');
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|||
|
||||
import { api } from '@renderer/api';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import { useTabIdOptional } from '@renderer/contexts/useTabUIContext';
|
||||
import { useExtensionsTabState } from '@renderer/hooks/useExtensionsTabState';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { Tabs, TabsContent, TabsList } from '@renderer/components/ui/tabs';
|
||||
import {
|
||||
Tooltip,
|
||||
|
|
@ -18,14 +15,17 @@ import {
|
|||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@renderer/components/ui/tooltip';
|
||||
import { useTabIdOptional } from '@renderer/contexts/useTabUIContext';
|
||||
import { useExtensionsTabState } from '@renderer/hooks/useExtensionsTabState';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { AlertTriangle, BookOpen, Info, Key, Plus, Puzzle, RefreshCw, Server } from 'lucide-react';
|
||||
|
||||
import { ApiKeysPanel } from './apikeys/ApiKeysPanel';
|
||||
import { ExtensionsSubTabTrigger } from './ExtensionsSubTabTrigger';
|
||||
import { CustomMcpServerDialog } from './mcp/CustomMcpServerDialog';
|
||||
import { McpServersPanel } from './mcp/McpServersPanel';
|
||||
import { PluginsPanel } from './plugins/PluginsPanel';
|
||||
import { SkillsPanel } from './skills/SkillsPanel';
|
||||
import { ExtensionsSubTabTrigger } from './ExtensionsSubTabTrigger';
|
||||
|
||||
export const ExtensionStoreView = (): React.JSX.Element => {
|
||||
const tabId = useTabIdOptional();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
import { TabsTrigger } from '@renderer/components/ui/tabs';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
interface ExtensionsSubTabTriggerProps {
|
||||
value: 'plugins' | 'mcp-servers' | 'skills' | 'api-keys';
|
||||
label: string;
|
||||
|
|
@ -21,7 +20,7 @@ export const ExtensionsSubTabTrigger = ({
|
|||
return (
|
||||
<TabsTrigger
|
||||
value={value}
|
||||
className="relative gap-1.5 rounded-b-none pr-7 data-[state=active]:z-10 data-[state=active]:-mb-px data-[state=active]:bg-[var(--color-surface)] data-[state=active]:shadow-none data-[state=active]:after:absolute data-[state=active]:after:-bottom-px data-[state=active]:after:left-0 data-[state=active]:after:right-0 data-[state=active]:after:h-1 data-[state=active]:after:bg-[var(--color-surface)] data-[state=active]:after:content-['']"
|
||||
className="relative gap-1.5 rounded-b-none pr-7 data-[state=active]:z-10 data-[state=active]:-mb-px data-[state=active]:bg-[var(--color-surface)] data-[state=active]:shadow-none data-[state=active]:after:absolute data-[state=active]:after:inset-x-0 data-[state=active]:after:-bottom-px data-[state=active]:after:h-1 data-[state=active]:after:bg-[var(--color-surface)] data-[state=active]:after:content-['']"
|
||||
>
|
||||
<Icon className="size-3.5" />
|
||||
{label}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
TooltipTrigger,
|
||||
} from '@renderer/components/ui/tooltip';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { Copy, Check, Pencil, Trash2 } from 'lucide-react';
|
||||
import { Check, Copy, Pencil, Trash2 } from 'lucide-react';
|
||||
|
||||
import type { ApiKeyEntry } from '@shared/types/extensions';
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Check, Loader2, Trash2 } from 'lucide-react';
|
||||
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import {
|
||||
Tooltip,
|
||||
|
|
@ -15,6 +13,7 @@ import {
|
|||
TooltipTrigger,
|
||||
} from '@renderer/components/ui/tooltip';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { Check, Loader2, Trash2 } from 'lucide-react';
|
||||
|
||||
import type { ExtensionOperationState } from '@shared/types/extensions';
|
||||
|
||||
|
|
@ -28,7 +27,7 @@ interface InstallButtonProps {
|
|||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export function InstallButton({
|
||||
export const InstallButton = ({
|
||||
state,
|
||||
isInstalled,
|
||||
onInstall,
|
||||
|
|
@ -36,7 +35,7 @@ export function InstallButton({
|
|||
disabled,
|
||||
size = 'sm',
|
||||
errorMessage,
|
||||
}: InstallButtonProps) {
|
||||
}: InstallButtonProps) => {
|
||||
const cliStatus = useStore((s) => s.cliStatus);
|
||||
const cliMissing = cliStatus !== null && !cliStatus.installed;
|
||||
const isDisabled = disabled || cliMissing;
|
||||
|
|
@ -52,7 +51,7 @@ export function InstallButton({
|
|||
if (state === 'pending') {
|
||||
return (
|
||||
<Button size={size} variant="outline" disabled>
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
<span className="ml-1.5">
|
||||
{pendingAction === 'uninstall' ? 'Removing...' : 'Installing...'}
|
||||
</span>
|
||||
|
|
@ -63,7 +62,7 @@ export function InstallButton({
|
|||
if (state === 'success') {
|
||||
return (
|
||||
<Button size={size} variant="outline" disabled className="text-green-400">
|
||||
<Check className="h-3.5 w-3.5" />
|
||||
<Check className="size-3.5" />
|
||||
<span className="ml-1.5">Done</span>
|
||||
</Button>
|
||||
);
|
||||
|
|
@ -121,7 +120,7 @@ export function InstallButton({
|
|||
}}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
<Trash2 className="size-3.5" />
|
||||
<span className="ml-1.5">Uninstall</span>
|
||||
</Button>
|
||||
) : (
|
||||
|
|
@ -153,4 +152,4 @@ export function InstallButton({
|
|||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
* InstallCountBadge — formatted download count with icon.
|
||||
*/
|
||||
|
||||
import { Download } from 'lucide-react';
|
||||
|
||||
import { formatInstallCount } from '@shared/utils/extensionNormalizers';
|
||||
import { Download } from 'lucide-react';
|
||||
|
||||
interface InstallCountBadgeProps {
|
||||
count: number;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { api } from '@renderer/api';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -23,7 +24,6 @@ import {
|
|||
SelectValue,
|
||||
} from '@renderer/components/ui/select';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { api } from '@renderer/api';
|
||||
import { Plus, Server, Trash2 } from 'lucide-react';
|
||||
|
||||
import type {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,18 @@
|
|||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { api } from '@renderer/api';
|
||||
import { Badge } from '@renderer/components/ui/badge';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { api } from '@renderer/api';
|
||||
import { formatCompactNumber, formatRelativeTime } from '@renderer/utils/formatters';
|
||||
import { Cloud, Clock, Globe, KeyRound, Lock, Monitor, Star, Tag, Wrench } from 'lucide-react';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated -- lucide naming migration, alias is stable
|
||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||
import { Clock, Cloud, Globe, KeyRound, Lock, Monitor, Star, Tag, Wrench } from 'lucide-react';
|
||||
import { Github as GithubIcon } from 'lucide-react';
|
||||
|
||||
import { InstallButton } from '../common/InstallButton';
|
||||
import { SourceBadge } from '../common/SourceBadge';
|
||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||
|
||||
import type { McpCatalogItem, McpServerDiagnostic } from '@shared/types/extensions';
|
||||
|
||||
|
|
@ -82,7 +80,7 @@ export const McpServerCard = ({
|
|||
{hasIcon && (
|
||||
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-surface-raised">
|
||||
<img
|
||||
src={server.iconUrl!}
|
||||
src={server.iconUrl}
|
||||
alt=""
|
||||
className="size-7 rounded object-contain"
|
||||
onError={() => setImgError(true)}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { api } from '@renderer/api';
|
||||
import { Badge } from '@renderer/components/ui/badge';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
|
|
@ -12,8 +15,6 @@ import {
|
|||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@renderer/components/ui/dialog';
|
||||
import { Badge } from '@renderer/components/ui/badge';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import { Input } from '@renderer/components/ui/input';
|
||||
import { Label } from '@renderer/components/ui/label';
|
||||
import {
|
||||
|
|
@ -24,12 +25,11 @@ import {
|
|||
SelectValue,
|
||||
} from '@renderer/components/ui/select';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { api } from '@renderer/api';
|
||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||
import { ExternalLink, Lock, Plus, Star, Trash2, Wrench } from 'lucide-react';
|
||||
|
||||
import { InstallButton } from '../common/InstallButton';
|
||||
import { SourceBadge } from '../common/SourceBadge';
|
||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||
|
||||
import type { McpCatalogItem, McpHeaderDef, McpServerDiagnostic } from '@shared/types/extensions';
|
||||
|
||||
|
|
@ -214,7 +214,7 @@ export const McpServerDetailDialog = ({
|
|||
{hasIcon && (
|
||||
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg border border-border bg-surface-raised">
|
||||
<img
|
||||
src={server.iconUrl!}
|
||||
src={server.iconUrl}
|
||||
alt=""
|
||||
className="size-8 rounded object-contain"
|
||||
onError={() => setImgError(true)}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
} from '@renderer/components/ui/select';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { formatRelativeTime } from '@renderer/utils/formatters';
|
||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||
import { AlertTriangle, RefreshCw, Search, Server } from 'lucide-react';
|
||||
|
||||
import { SearchInput } from '../common/SearchInput';
|
||||
|
|
@ -27,7 +28,6 @@ import type {
|
|||
McpCatalogItem,
|
||||
McpServerDiagnostic,
|
||||
} from '@shared/types/extensions';
|
||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||
|
||||
type McpSortValue = 'name-asc' | 'name-desc' | 'tools-desc';
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ export const PluginCard = ({ plugin, index, onClick }: PluginCardProps): React.J
|
|||
}`}
|
||||
>
|
||||
{plugin.source === 'official' && (
|
||||
<div className="pointer-events-none absolute -left-[1px] -top-[1px] size-16 overflow-hidden">
|
||||
<div className="absolute left-[-24px] top-[4px] w-[80px] rotate-[-45deg] bg-blue-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
|
||||
<div className="pointer-events-none absolute -left-px -top-px size-16 overflow-hidden">
|
||||
<div className="absolute left-[-24px] top-[4px] w-[80px] -rotate-45 bg-blue-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
|
||||
Official
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { api } from '@renderer/api';
|
||||
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
||||
import { Badge } from '@renderer/components/ui/badge';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
|
|
@ -11,8 +15,6 @@ import {
|
|||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@renderer/components/ui/dialog';
|
||||
import { Badge } from '@renderer/components/ui/badge';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import { Label } from '@renderer/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
|
|
@ -22,7 +24,6 @@ import {
|
|||
SelectValue,
|
||||
} from '@renderer/components/ui/select';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { api } from '@renderer/api';
|
||||
import {
|
||||
getCapabilityLabel,
|
||||
inferCapabilities,
|
||||
|
|
@ -34,8 +35,6 @@ import { InstallButton } from '../common/InstallButton';
|
|||
import { InstallCountBadge } from '../common/InstallCountBadge';
|
||||
import { SourceBadge } from '../common/SourceBadge';
|
||||
|
||||
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
||||
|
||||
import type { EnrichedPlugin, InstallScope } from '@shared/types/extensions';
|
||||
|
||||
interface PluginDetailDialogProps {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import {
|
|||
keymap,
|
||||
lineNumbers,
|
||||
} from '@codemirror/view';
|
||||
import { baseEditorTheme } from '@renderer/utils/codemirrorTheme';
|
||||
import { getSyncLanguageExtension } from '@renderer/utils/codemirrorLanguages';
|
||||
import { baseEditorTheme } from '@renderer/utils/codemirrorTheme';
|
||||
|
||||
const skillEditorTheme = EditorView.theme({
|
||||
'&': {
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ import { useStore } from '@renderer/store';
|
|||
import { FileSearch, RotateCcw, X } from 'lucide-react';
|
||||
|
||||
import { SkillCodeEditor } from './SkillCodeEditor';
|
||||
import { SkillReviewDialog } from './SkillReviewDialog';
|
||||
import {
|
||||
buildSkillDraftFiles,
|
||||
buildSkillTemplate,
|
||||
readSkillTemplateContent,
|
||||
updateSkillTemplateFrontmatter,
|
||||
} from './skillDraftUtils';
|
||||
import { SkillReviewDialog } from './SkillReviewDialog';
|
||||
|
||||
import type {
|
||||
SkillDetail,
|
||||
|
|
@ -792,7 +792,7 @@ export const SkillEditorDialog = ({
|
|||
<X className="mr-1.5 size-3.5" />
|
||||
Cancel
|
||||
</Button>
|
||||
<div className="min-w-[16rem] flex-1">
|
||||
<div className="min-w-64 flex-1">
|
||||
<p className="text-sm text-text-muted">
|
||||
Review the file changes first, then confirm save in the next step.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ export const SkillImportDialog = ({
|
|||
<X className="mr-1.5 size-3.5" />
|
||||
Cancel
|
||||
</Button>
|
||||
<p className="min-w-[16rem] flex-1 text-sm text-text-muted">
|
||||
<p className="min-w-64 flex-1 text-sm text-text-muted">
|
||||
Review the copied files first, then confirm the import in the next step.
|
||||
</p>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { api } from '@renderer/api';
|
|||
import { Badge } from '@renderer/components/ui/badge';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { useStore } from '@renderer/store';
|
||||
import {
|
||||
AlertTriangle,
|
||||
ArrowUpAZ,
|
||||
|
|
@ -25,8 +25,8 @@ import { SkillDetailDialog } from './SkillDetailDialog';
|
|||
import { SkillEditorDialog } from './SkillEditorDialog';
|
||||
import { SkillImportDialog } from './SkillImportDialog';
|
||||
|
||||
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
||||
import type { SkillsSortState } from '@renderer/hooks/useExtensionsTabState';
|
||||
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
||||
|
||||
const SUCCESS_BANNER_MS = 2500;
|
||||
const NEW_SKILL_HIGHLIGHT_MS = 4000;
|
||||
|
|
@ -233,7 +233,7 @@ export const SkillsPanel = ({
|
|||
|
||||
<div className="flex w-full flex-col gap-3 xl:w-auto xl:min-w-[32rem] xl:max-w-[40rem]">
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:flex-wrap lg:items-center xl:justify-end">
|
||||
<div className="w-full lg:min-w-[18rem] lg:flex-1 xl:w-80 xl:flex-none">
|
||||
<div className="w-full lg:min-w-72 lg:flex-1 xl:w-80 xl:flex-none">
|
||||
<SearchInput
|
||||
value={skillsSearchQuery}
|
||||
onChange={setSkillsSearchQuery}
|
||||
|
|
@ -318,7 +318,7 @@ export const SkillsPanel = ({
|
|||
['personal', 'Personal'],
|
||||
['needs-attention', 'Needs attention'],
|
||||
['has-scripts', 'Has scripts'],
|
||||
] as Array<[SkillsQuickFilter, string]>
|
||||
] as [SkillsQuickFilter, string][]
|
||||
).map(([value, label]) => (
|
||||
<Button
|
||||
key={value}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export function buildSkillTemplate(input: SkillDraftTemplateInput): string {
|
|||
|
||||
export function readSkillTemplateContent(rawContent: string): SkillTemplateParseResult {
|
||||
const content = rawContent.replace(/^\uFEFF/u, '');
|
||||
const match = content.match(SKILL_FRONTMATTER_PATTERN);
|
||||
const match = SKILL_FRONTMATTER_PATTERN.exec(content);
|
||||
if (!match) {
|
||||
return {
|
||||
hasStructuredSections: false,
|
||||
|
|
@ -130,7 +130,7 @@ export function updateSkillTemplateFrontmatter(
|
|||
input: SkillDraftTemplateInput
|
||||
): string {
|
||||
const content = rawContent.replace(/^\uFEFF/u, '');
|
||||
const match = content.match(SKILL_FRONTMATTER_PATTERN);
|
||||
const match = SKILL_FRONTMATTER_PATTERN.exec(content);
|
||||
const body = match ? (match[2] ?? '') : content;
|
||||
|
||||
let data: Record<string, unknown> = {};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useMemo, useState } from 'react';
|
|||
|
||||
import { isElectronMode } from '@renderer/api';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { Bell, Puzzle, Settings, Users, PanelRight } from 'lucide-react';
|
||||
import { Bell, PanelRight, Puzzle, Settings, Users } from 'lucide-react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
import { MoreMenu } from './MoreMenu';
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ import {
|
|||
Zap,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { LaunchTeamDialog } from '../team/dialogs/LaunchTeamDialog';
|
||||
import { ScheduleRunLogDialog } from '../team/schedule/ScheduleRunLogDialog';
|
||||
import { ScheduleRunRow } from '../team/schedule/ScheduleRunRow';
|
||||
import { ScheduleStatusBadge } from '../team/schedule/ScheduleStatusBadge';
|
||||
import { LaunchTeamDialog } from '../team/dialogs/LaunchTeamDialog';
|
||||
|
||||
import type { Schedule, ScheduleRun, ScheduleStatus } from '@shared/types';
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ import type { Schedule, ScheduleRun, ScheduleStatus } from '@shared/types';
|
|||
// Constants
|
||||
// =============================================================================
|
||||
|
||||
const STATUS_OPTIONS: Array<{ value: ScheduleStatus | 'all'; label: string }> = [
|
||||
const STATUS_OPTIONS: { value: ScheduleStatus | 'all'; label: string }[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'active', label: 'Active' },
|
||||
{ value: 'paused', label: 'Paused' },
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ export const ConfigEditorDialog = ({
|
|||
keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap, ...searchKeymap]),
|
||||
baseEditorTheme,
|
||||
configEditorTheme,
|
||||
// eslint-disable-next-line sonarjs/no-nested-functions -- CodeMirror listener callback within useEffect setup
|
||||
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (update.docChanged) {
|
||||
const text = update.state.doc.toString();
|
||||
|
|
|
|||
|
|
@ -14,18 +14,18 @@ import {
|
|||
ArrowRightLeft,
|
||||
Bell,
|
||||
BellRing,
|
||||
CheckCircle2,
|
||||
CirclePlus,
|
||||
Clock,
|
||||
ExternalLink,
|
||||
EyeOff,
|
||||
GitBranch,
|
||||
HelpCircle,
|
||||
Inbox,
|
||||
Info,
|
||||
Mail,
|
||||
MessageSquare,
|
||||
PartyPopper,
|
||||
CirclePlus,
|
||||
CheckCircle2,
|
||||
GitBranch,
|
||||
Send,
|
||||
Users,
|
||||
Volume2,
|
||||
|
|
@ -39,7 +39,9 @@ import type { NotificationTrigger } from '@renderer/types/data';
|
|||
import type { TeamReviewState, TeamTaskStatus } from '@shared/types';
|
||||
|
||||
/** Notification targets span workflow status plus the explicit review axis. */
|
||||
type NotifiableStatus = TeamTaskStatus | Extract<TeamReviewState, 'review' | 'needsFix' | 'approved'>;
|
||||
type NotifiableStatus =
|
||||
| TeamTaskStatus
|
||||
| Extract<TeamReviewState, 'review' | 'needsFix' | 'approved'>;
|
||||
|
||||
// Snooze duration options
|
||||
const SNOOZE_OPTIONS = [
|
||||
|
|
|
|||
|
|
@ -358,7 +358,6 @@ export const GlobalTaskList = ({
|
|||
// Reset showArchived when archive becomes empty
|
||||
useEffect(() => {
|
||||
if (showArchived && !hasArchivedTasks) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- intentional sync on prop change
|
||||
setShowArchived(false);
|
||||
}
|
||||
}, [showArchived, hasArchivedTasks]);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { getTeamColorSet } from '@renderer/constants/teamColors';
|
|||
import { useTheme } from '@renderer/hooks/useTheme';
|
||||
import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { REVIEW_STATE_DISPLAY, buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { buildMemberColorMap, REVIEW_STATE_DISPLAY } from '@renderer/utils/memberHelpers';
|
||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||
import { projectColor } from '@renderer/utils/projectColor';
|
||||
import { projectLabelFromPath } from '@renderer/utils/taskGrouping';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer
|
|||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { REVIEW_STATE_DISPLAY, buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { buildMemberColorMap, REVIEW_STATE_DISPLAY } from '@renderer/utils/memberHelpers';
|
||||
import { linkifyTaskIdsInMarkdown } from '@renderer/utils/taskReferenceUtils';
|
||||
import { getTaskKanbanColumn } from '@shared/utils/reviewState';
|
||||
import { formatTaskDisplayLabel, taskMatchesRef } from '@shared/utils/taskIdentity';
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import {
|
|||
type TaskChangeRequestOptions,
|
||||
} from '@renderer/utils/taskChangeRequest';
|
||||
import { stripAgentBlocks } from '@shared/constants/agentBlocks';
|
||||
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
|
||||
import { deriveTaskDisplayId, formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
|
||||
import {
|
||||
AlertTriangle,
|
||||
|
|
@ -50,11 +51,9 @@ import {
|
|||
UserPlus,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
import { AddMemberDialog } from './dialogs/AddMemberDialog';
|
||||
import type { AddMemberEntry } from './dialogs/AddMemberDialog';
|
||||
import { CreateTaskDialog } from './dialogs/CreateTaskDialog';
|
||||
import { EditTeamDialog } from './dialogs/EditTeamDialog';
|
||||
import { LaunchTeamDialog } from './dialogs/LaunchTeamDialog';
|
||||
|
|
@ -67,16 +66,18 @@ import { KanbanSearchInput } from './kanban/KanbanSearchInput';
|
|||
import { TrashDialog } from './kanban/TrashDialog';
|
||||
import { MemberDetailDialog } from './members/MemberDetailDialog';
|
||||
|
||||
import type { AddMemberEntry } from './dialogs/AddMemberDialog';
|
||||
|
||||
const ProjectEditorOverlay = lazy(() =>
|
||||
import('./editor/ProjectEditorOverlay').then((m) => ({ default: m.ProjectEditorOverlay }))
|
||||
);
|
||||
import { MemberList } from './members/MemberList';
|
||||
import { MessagesPanel } from './messages/MessagesPanel';
|
||||
import { ChangeReviewDialog } from './review/ChangeReviewDialog';
|
||||
import { ScheduleSection } from './schedule/ScheduleSection';
|
||||
import { ClaudeLogsSection } from './ClaudeLogsSection';
|
||||
import { CollapsibleTeamSection } from './CollapsibleTeamSection';
|
||||
import { ProcessesSection } from './ProcessesSection';
|
||||
import { ScheduleSection } from './schedule/ScheduleSection';
|
||||
import { TeamProvisioningBanner } from './TeamProvisioningBanner';
|
||||
import { TeamSessionsSection } from './TeamSessionsSection';
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export const TeamProvisioningBanner = ({
|
|||
|
||||
if (isReady) {
|
||||
const readyMessage = allTeammatesOnline
|
||||
? `Team launched — all ${teamMembers!.length} teammates online`
|
||||
? `Team launched — all ${teamMembers.length} teammates online`
|
||||
: 'Team launched — teammates may still be starting';
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { useTheme } from '@renderer/hooks/useTheme';
|
|||
import { useStore } from '@renderer/store';
|
||||
import { FileText, Search, Terminal } from 'lucide-react';
|
||||
|
||||
import type { ToolApprovalRequest } from '@shared/types';
|
||||
|
||||
import { ToolApprovalSettingsPanel } from './dialogs/ToolApprovalSettingsPanel';
|
||||
|
||||
import type { ToolApprovalRequest } from '@shared/types';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tool icon mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export const ActiveTasksBlock = ({
|
|||
className="size-5 rounded-full bg-[var(--color-surface-raised)]"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex h-2.5 w-2.5">
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||
<span
|
||||
className={`absolute inline-flex size-full animate-ping rounded-full ${dotPing} opacity-70`}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -99,13 +99,13 @@ export function getCrossTeamSentMemberName(value: string | undefined): string |
|
|||
return parseQualifiedRecipient(value)?.memberName ?? null;
|
||||
}
|
||||
|
||||
function CrossTeamTeamBadge({
|
||||
const CrossTeamTeamBadge = ({
|
||||
teamName,
|
||||
onClick,
|
||||
}: {
|
||||
teamName: string;
|
||||
onClick?: (teamName: string) => void;
|
||||
}): React.JSX.Element {
|
||||
}): React.JSX.Element => {
|
||||
if (onClick) {
|
||||
return (
|
||||
<button
|
||||
|
|
@ -135,7 +135,7 @@ function CrossTeamTeamBadge({
|
|||
{teamName}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface ActivityItemProps {
|
||||
message: InboxMessage;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ import {
|
|||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||
import { Layers } from 'lucide-react';
|
||||
|
||||
import { buildMessageContext, resolveMessageRenderProps } from './activityMessageContext';
|
||||
|
||||
import { ActivityItem, isNoiseMessage } from './ActivityItem';
|
||||
import { buildMessageContext, resolveMessageRenderProps } from './activityMessageContext';
|
||||
import { AnimatedHeightReveal } from './AnimatedHeightReveal';
|
||||
import { findNewestMessageIndex, resolveTimelineCollapseState } from './collapseState';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ import {
|
|||
areThoughtMessagesEquivalentForRender,
|
||||
} from '@renderer/utils/messageRenderEquality';
|
||||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||
import { formatToolSummary, parseToolSummary } from '@shared/utils/toolSummary';
|
||||
import { extractMarkdownPlainText } from '@shared/utils/markdownTextSearch';
|
||||
import { formatToolSummary, parseToolSummary } from '@shared/utils/toolSummary';
|
||||
import { ChevronDown, ChevronRight, ChevronUp, Maximize2 } from 'lucide-react';
|
||||
|
||||
import {
|
||||
AnimatedHeightReveal,
|
||||
ENTRY_REVEAL_ANIMATION_MS,
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ import { CARD_ICON_MUTED } from '@renderer/constants/cssVariables';
|
|||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
||||
|
||||
import { MemberBadge } from '../MemberBadge';
|
||||
|
||||
import { ActivityItem } from './ActivityItem';
|
||||
import { buildMessageContext, resolveMessageRenderProps } from './activityMessageContext';
|
||||
import { MemberBadge } from '../MemberBadge';
|
||||
import { ThoughtBodyContent } from './ThoughtBodyContent';
|
||||
|
||||
import type { TimelineItem, LeadThoughtGroup } from './LeadThoughtsGroup';
|
||||
import type { LeadThoughtGroup, TimelineItem } from './LeadThoughtsGroup';
|
||||
import type { InboxMessage, ResolvedTeamMember } from '@shared/types';
|
||||
|
||||
function formatTime(timestamp: string): string {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export const PendingRepliesBlock = ({
|
|||
className="size-5 rounded-full bg-[var(--color-surface-raised)]"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex h-2.5 w-2.5">
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
||||
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
||||
</span>
|
||||
|
|
@ -151,7 +151,7 @@ export const PendingRepliesBlock = ({
|
|||
<div className="flex items-center gap-2 px-3 py-2">
|
||||
<span className="relative inline-flex shrink-0 items-center justify-center rounded-full bg-[var(--color-surface-raised)] p-1">
|
||||
<Users size={12} style={{ color: colors.border }} />
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex h-2.5 w-2.5">
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
||||
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer
|
|||
import { CopyButton } from '@renderer/components/common/CopyButton';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { CARD_ICON_MUTED, CARD_TEXT_LIGHT } from '@renderer/constants/cssVariables';
|
||||
import { linkifyAllMentionsInMarkdown } from '@renderer/utils/mentionLinkify';
|
||||
import {
|
||||
areStringArraysEqual,
|
||||
areStringMapsEqual,
|
||||
areThoughtMessagesEquivalentForRender,
|
||||
} from '@renderer/utils/messageRenderEquality';
|
||||
import { linkifyAllMentionsInMarkdown } from '@renderer/utils/mentionLinkify';
|
||||
import { linkifyTaskIdsInMarkdown, parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
||||
import { Reply } from 'lucide-react';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import 'yet-another-react-lightbox/styles.css';
|
||||
import 'yet-another-react-lightbox/plugins/counter.css';
|
||||
|
||||
import { createContext, useContext, useEffect, useRef, useMemo } from 'react';
|
||||
import { createContext, useContext, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
import Lightbox from 'yet-another-react-lightbox';
|
||||
import Counter from 'yet-another-react-lightbox/plugins/counter';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Info } from 'lucide-react';
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
import { AttachmentDisplay } from './AttachmentDisplay';
|
||||
|
||||
|
|
@ -27,9 +26,7 @@ export const SourceMessageAttachments = ({
|
|||
}));
|
||||
|
||||
const truncatedText =
|
||||
sourceMessage.text.length > 300
|
||||
? sourceMessage.text.slice(0, 297) + '...'
|
||||
: sourceMessage.text;
|
||||
sourceMessage.text.length > 300 ? sourceMessage.text.slice(0, 297) + '...' : sourceMessage.text;
|
||||
|
||||
const formattedDate = (() => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { getNextSuggestedMemberName } from '@renderer/components/team/members/memberNameSets';
|
||||
import {
|
||||
buildMembersFromDrafts,
|
||||
createMemberDraft,
|
||||
MembersEditorSection,
|
||||
validateMemberNameInline,
|
||||
} from '@renderer/components/team/members/MembersEditorSection';
|
||||
import { getNextSuggestedMemberName } from '@renderer/components/team/members/memberNameSets';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ import { LimitContextCheckbox } from './LimitContextCheckbox';
|
|||
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
||||
import { ProjectPathSelector } from './ProjectPathSelector';
|
||||
import { SkipPermissionsCheckbox } from './SkipPermissionsCheckbox';
|
||||
import { getNextSuggestedTeamName } from './teamNameSets';
|
||||
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
|
||||
import { getNextSuggestedTeamName } from './teamNameSets';
|
||||
|
||||
import type { MemberDraft } from '@renderer/components/team/members/membersEditorTypes';
|
||||
|
||||
|
|
@ -694,7 +694,7 @@ export const CreateTeamDialog = ({
|
|||
if (!validation.valid) {
|
||||
const errors = validation.errors ?? {};
|
||||
setFieldErrors(errors);
|
||||
const messages = Object.values(errors).filter(Boolean) as string[];
|
||||
const messages = Object.values(errors).filter(Boolean);
|
||||
setLocalError(messages.join(' · ') || 'Check form fields');
|
||||
return;
|
||||
}
|
||||
|
|
@ -745,7 +745,7 @@ export const CreateTeamDialog = ({
|
|||
if (!prev.teamName) return prev;
|
||||
// eslint-disable-next-line sonarjs/no-unused-vars -- destructured to omit teamName from rest
|
||||
const { teamName: _teamName, ...rest } = prev;
|
||||
const remaining = Object.values(rest).filter(Boolean) as string[];
|
||||
const remaining = Object.values(rest).filter(Boolean);
|
||||
if (remaining.length === 0) {
|
||||
setLocalError(null);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ import {
|
|||
import { Input } from '@renderer/components/ui/input';
|
||||
import { Label } from '@renderer/components/ui/label';
|
||||
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||
import { useChipDraftPersistence } from '@renderer/hooks/useChipDraftPersistence';
|
||||
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
|
||||
import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer';
|
||||
import { useTheme } from '@renderer/hooks/useTheme';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { normalizePath } from '@renderer/utils/pathNormalize';
|
||||
|
|
@ -38,12 +38,13 @@ import {
|
|||
X,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { CronScheduleInput } from '../schedule/CronScheduleInput';
|
||||
|
||||
import { AdvancedCliSection } from './AdvancedCliSection';
|
||||
import { EffortLevelSelector } from './EffortLevelSelector';
|
||||
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
||||
import { ProjectPathSelector } from './ProjectPathSelector';
|
||||
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
|
||||
import { CronScheduleInput } from '../schedule/CronScheduleInput';
|
||||
|
||||
import type { ActiveTeamRef } from './CreateTeamDialog';
|
||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||
|
|
@ -112,7 +113,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|||
const { isLight } = useTheme();
|
||||
const isLaunch = props.mode === 'launch';
|
||||
const isSchedule = props.mode === 'schedule';
|
||||
const schedule = isSchedule ? ((props as LaunchDialogScheduleMode).schedule ?? null) : null;
|
||||
const schedule = isSchedule ? (props.schedule ?? null) : null;
|
||||
const isEditing = isSchedule && !!schedule;
|
||||
|
||||
// Team name: always present for launch mode, may be absent in schedule mode (standalone page)
|
||||
|
|
@ -338,7 +339,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|||
// Clear stale provisioning error when dialog opens
|
||||
useEffect(() => {
|
||||
if (!open || !isLaunch) return;
|
||||
(props as LaunchDialogLaunchMode).clearProvisioningError?.(effectiveTeamName);
|
||||
props.clearProvisioningError?.(effectiveTeamName);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open, isLaunch, effectiveTeamName]);
|
||||
|
||||
|
|
@ -443,9 +444,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|||
}, [open, repositoryGroups]);
|
||||
|
||||
// Pre-select defaultProjectPath (launch mode) or first project
|
||||
const defaultProjectPath = isLaunch
|
||||
? (props as LaunchDialogLaunchMode).defaultProjectPath
|
||||
: undefined;
|
||||
const defaultProjectPath = isLaunch ? props.defaultProjectPath : undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || cwdMode !== 'project' || selectedProjectPath || projects.length === 0) return;
|
||||
|
|
@ -466,7 +465,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|||
// Launch-only: conflict detection
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const activeTeams = isLaunch ? (props as LaunchDialogLaunchMode).activeTeams : undefined;
|
||||
const activeTeams = isLaunch ? props.activeTeams : undefined;
|
||||
|
||||
const conflictingTeam = useMemo(() => {
|
||||
if (!isLaunch || !activeTeams?.length || !effectiveCwd) return null;
|
||||
|
|
@ -487,7 +486,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
const storeMembers = useStore((s) => s.selectedTeamData?.members ?? []);
|
||||
const members = isLaunch ? (props as LaunchDialogLaunchMode).members : storeMembers;
|
||||
const members = isLaunch ? props.members : storeMembers;
|
||||
|
||||
const colorMap = useMemo(() => buildMemberColorMap(members), [members]);
|
||||
const mentionSuggestions = useMemo<MentionSuggestion[]>(
|
||||
|
|
@ -564,7 +563,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|||
// Error
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const provisioningError = isLaunch ? (props as LaunchDialogLaunchMode).provisioningError : null;
|
||||
const provisioningError = isLaunch ? props.provisioningError : null;
|
||||
const activeError = localError ?? provisioningError;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -586,7 +585,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|||
void (async () => {
|
||||
try {
|
||||
if (isLaunch) {
|
||||
await (props as LaunchDialogLaunchMode).onLaunch({
|
||||
await props.onLaunch({
|
||||
teamName: effectiveTeamName,
|
||||
cwd: effectiveCwd,
|
||||
prompt: promptDraft.value.trim() || undefined,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
import { Checkbox } from '@renderer/components/ui/checkbox';
|
||||
import { Label } from '@renderer/components/ui/label';
|
||||
import {
|
||||
|
|
@ -10,6 +8,7 @@ import {
|
|||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@renderer/components/ui/tooltip';
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
interface LimitContextCheckboxProps {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export const OptionalSettingsSection = ({
|
|||
>
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-start justify-between gap-3 px-3 py-3 text-left transition-colors hover:bg-[var(--color-surface-raised)]"
|
||||
className="flex w-full items-start justify-between gap-3 p-3 text-left transition-colors hover:bg-[var(--color-surface-raised)]"
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export const WorkflowTimeline = ({ events, memberColorMap }: WorkflowTimelinePro
|
|||
{/* Content */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex w-full items-center gap-2 rounded px-1.5 py-1.5 text-xs text-[var(--color-text-secondary)]">
|
||||
<div className="flex w-full items-center gap-2 rounded p-1.5 text-xs text-[var(--color-text-secondary)]">
|
||||
<span className="shrink-0 font-mono text-[10px] text-[var(--color-text-muted)]">
|
||||
{time}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
||||
import { ImageLightbox } from '@renderer/components/team/attachments/ImageLightbox';
|
||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { useChipDraftPersistence } from '@renderer/hooks/useChipDraftPersistence';
|
||||
|
|
@ -7,10 +10,10 @@ import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
|
|||
import { useTaskSuggestions } from '@renderer/hooks/useTaskSuggestions';
|
||||
import { useTeamSuggestions } from '@renderer/hooks/useTeamSuggestions';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||
import { buildReplyBlock } from '@renderer/utils/agentMessageFormatting';
|
||||
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||
import {
|
||||
extractTaskRefsFromText,
|
||||
stripEncodedTaskReferenceMetadata,
|
||||
|
|
@ -18,10 +21,6 @@ import {
|
|||
import { MAX_TEXT_LENGTH } from '@shared/constants';
|
||||
import { ImagePlus, Mic, Send, Trash2, X } from 'lucide-react';
|
||||
|
||||
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
||||
import { ImageLightbox } from '@renderer/components/team/attachments/ImageLightbox';
|
||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||
|
||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||
import type { CommentAttachmentPayload, ResolvedTeamMember } from '@shared/types';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@ import {
|
|||
ImageLightbox,
|
||||
LightboxLockProvider,
|
||||
} from '@renderer/components/team/attachments/ImageLightbox';
|
||||
import {
|
||||
getTeamColorSet,
|
||||
getThemedBadge,
|
||||
getThemedBorder,
|
||||
getThemedText,
|
||||
} from '@renderer/constants/teamColors';
|
||||
import { CollapsibleTeamSection } from '@renderer/components/team/CollapsibleTeamSection';
|
||||
import { FileIcon } from '@renderer/components/team/editor/FileIcon';
|
||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||
|
|
@ -30,19 +24,25 @@ import { Input } from '@renderer/components/ui/input';
|
|||
import { MemberSelect } from '@renderer/components/ui/MemberSelect';
|
||||
import { TiptapEditor } from '@renderer/components/ui/tiptap';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { getLegacyCutoff, getReadCommentIds } from '@renderer/services/commentReadStorage';
|
||||
import { useStore } from '@renderer/store';
|
||||
import {
|
||||
getTeamColorSet,
|
||||
getThemedBadge,
|
||||
getThemedBorder,
|
||||
getThemedText,
|
||||
} from '@renderer/constants/teamColors';
|
||||
import { useTheme } from '@renderer/hooks/useTheme';
|
||||
import { useViewportCommentRead } from '@renderer/hooks/useViewportCommentRead';
|
||||
import { getLegacyCutoff, getReadCommentIds } from '@renderer/services/commentReadStorage';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { isImageMimeType } from '@renderer/utils/attachmentUtils';
|
||||
import {
|
||||
agentAvatarUrl,
|
||||
buildMemberColorMap,
|
||||
displayMemberName,
|
||||
KANBAN_COLUMN_DISPLAY,
|
||||
REVIEW_STATE_DISPLAY,
|
||||
TASK_STATUS_LABELS,
|
||||
TASK_STATUS_STYLES,
|
||||
agentAvatarUrl,
|
||||
displayMemberName,
|
||||
} from '@renderer/utils/memberHelpers';
|
||||
import { buildTaskChangeRequestOptions, deriveTaskSince } from '@renderer/utils/taskChangeRequest';
|
||||
import { linkifyTaskIdsInMarkdown, parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
||||
|
|
@ -78,9 +78,10 @@ import {
|
|||
X,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { SourceMessageAttachments } from '../attachments/SourceMessageAttachments';
|
||||
|
||||
import { WorkflowTimeline } from './StatusHistoryTimeline';
|
||||
import { TaskAttachments } from './TaskAttachments';
|
||||
import { SourceMessageAttachments } from '../attachments/SourceMessageAttachments';
|
||||
import { TaskCommentAwaitingReply } from './TaskCommentAwaitingReply';
|
||||
import { TaskCommentInput } from './TaskCommentInput';
|
||||
import { TaskCommentsSection } from './TaskCommentsSection';
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ import { Button } from '@renderer/components/ui/button';
|
|||
import { Checkbox } from '@renderer/components/ui/checkbox';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { Crown, Filter } from 'lucide-react';
|
||||
|
||||
import { displayMemberName } from '@renderer/utils/memberHelpers';
|
||||
import { Crown, Filter } from 'lucide-react';
|
||||
|
||||
import type { Session } from '@renderer/types/data';
|
||||
import type { KanbanColumnId, ResolvedTeamMember } from '@shared/types';
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export const KanbanSearchInput = ({
|
|||
|
||||
// Detect `#` trigger and extract filter text after it
|
||||
const hashMatch = useMemo(() => {
|
||||
const match = value.match(/#(\S*)$/);
|
||||
const match = /#(\S*)$/.exec(value);
|
||||
return match ? match[1] : null;
|
||||
}, [value]);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui
|
|||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { REVIEW_STATE_DISPLAY, buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { buildMemberColorMap, REVIEW_STATE_DISPLAY } from '@renderer/utils/memberHelpers';
|
||||
import {
|
||||
buildTaskChangePresenceKey,
|
||||
buildTaskChangeRequestOptions,
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ export const MemberDraftRow = ({
|
|||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 shrink-0 border-red-500/40 px-0 text-red-300 hover:bg-red-500/10 hover:text-red-200"
|
||||
className="size-8 shrink-0 border-red-500/40 px-0 text-red-300 hover:bg-red-500/10 hover:text-red-200"
|
||||
aria-label={`Remove ${member.name || `member ${index + 1}`}`}
|
||||
title="Remove member"
|
||||
onClick={() => onRemove(member.id)}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ import {
|
|||
SubagentRecentMessagesPreview,
|
||||
} from '@renderer/components/team/members/SubagentRecentMessagesPreview';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||
import { asEnhancedChunkArray } from '@renderer/types/data';
|
||||
import { enhanceAIGroup } from '@renderer/utils/aiGroupEnhancer';
|
||||
import { formatDuration } from '@renderer/utils/formatters';
|
||||
import { transformChunksToConversation } from '@renderer/utils/groupTransformer';
|
||||
import { getMemberColorByName } from '@shared/constants/memberColors';
|
||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||
import {
|
||||
AlertCircle,
|
||||
ChevronDown,
|
||||
|
|
@ -271,7 +271,12 @@ export const MemberLogsTab = ({
|
|||
if (!previewLog) return [];
|
||||
|
||||
// Use task-scoped recentPreviews when available
|
||||
if (previewLog.recentPreviews && previewLog.recentPreviews.length > 0 && taskWorkIntervals && taskWorkIntervals.length > 0) {
|
||||
if (
|
||||
previewLog.recentPreviews &&
|
||||
previewLog.recentPreviews.length > 0 &&
|
||||
taskWorkIntervals &&
|
||||
taskWorkIntervals.length > 0
|
||||
) {
|
||||
const GRACE_BEFORE = 30_000;
|
||||
const GRACE_AFTER = 15_000;
|
||||
const now = Date.now();
|
||||
|
|
@ -280,7 +285,10 @@ export const MemberLogsTab = ({
|
|||
const s = Date.parse(i.startedAt);
|
||||
if (!Number.isFinite(s)) return null;
|
||||
const e = typeof i.completedAt === 'string' ? Date.parse(i.completedAt) : null;
|
||||
return { startMs: s - GRACE_BEFORE, endMs: e != null && Number.isFinite(e) ? e + GRACE_AFTER : now + GRACE_AFTER };
|
||||
return {
|
||||
startMs: s - GRACE_BEFORE,
|
||||
endMs: e != null && Number.isFinite(e) ? e + GRACE_AFTER : now + GRACE_AFTER,
|
||||
};
|
||||
})
|
||||
.filter((v): v is { startMs: number; endMs: number } => v !== null);
|
||||
|
||||
|
|
@ -291,9 +299,7 @@ export const MemberLogsTab = ({
|
|||
return intervals.some((i) => ms >= i.startMs && ms <= i.endMs);
|
||||
});
|
||||
if (scoped.length > 0) {
|
||||
return scoped
|
||||
.reverse()
|
||||
.map((p, idx) => ({
|
||||
return scoped.reverse().map((p, idx) => ({
|
||||
id: `${previewLog.sessionId}:recent:${idx}`,
|
||||
timestamp: new Date(p.timestamp),
|
||||
kind: 'output' as const,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { CUSTOM_ROLE, NO_ROLE } from '@renderer/constants/teamRoles';
|
||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
|
||||
import type { MemberDraft } from './membersEditorTypes';
|
||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||
|
|
@ -37,7 +37,7 @@ export function createMemberDraft(initial?: Partial<MemberDraft>): MemberDraft {
|
|||
}
|
||||
|
||||
export function buildMemberDraftColorMap(
|
||||
members: ReadonlyArray<Pick<MemberDraft, 'name'>>
|
||||
members: readonly Pick<MemberDraft, 'name'>[]
|
||||
): Map<string, string> {
|
||||
return buildMemberColorMap(
|
||||
members
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { cn } from '@renderer/lib/utils';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@renderer/components/ui/tooltip';
|
||||
import { cn } from '@renderer/lib/utils';
|
||||
|
||||
import type { AgentActionMode } from '@shared/types';
|
||||
|
||||
export type ActionMode = AgentActionMode;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|||
import { api } from '@renderer/api';
|
||||
import { AttachmentPreviewList } from '@renderer/components/team/attachments/AttachmentPreviewList';
|
||||
import { DropZoneOverlay } from '@renderer/components/team/attachments/DropZoneOverlay';
|
||||
import { ActionModeSelector } from '@renderer/components/team/messages/ActionModeSelector';
|
||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||
import { ActionModeSelector } from '@renderer/components/team/messages/ActionModeSelector';
|
||||
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||
import { useComposerDraft } from '@renderer/hooks/useComposerDraft';
|
||||
import { useTaskSuggestions } from '@renderer/hooks/useTaskSuggestions';
|
||||
import { useTeamSuggestions } from '@renderer/hooks/useTeamSuggestions';
|
||||
|
|
@ -17,7 +18,6 @@ import { isTeamProvisioningActive } from '@renderer/store/slices/teamSlice';
|
|||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||
import {
|
||||
extractTaskRefsFromText,
|
||||
|
|
@ -27,8 +27,8 @@ import { MAX_TEXT_LENGTH } from '@shared/constants';
|
|||
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||
import { AlertCircle, Check, ChevronDown, ImagePlus, Mic, Search, Send } from 'lucide-react';
|
||||
|
||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||
import type { ActionMode } from '@renderer/components/team/messages/ActionModeSelector';
|
||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||
import type {
|
||||
AttachmentPayload,
|
||||
ResolvedTeamMember,
|
||||
|
|
@ -84,7 +84,6 @@ export const MessageComposer = ({
|
|||
(externalTextareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [externalTextareaRef]);
|
||||
const [recipient, setRecipient] = useState<string>(() => {
|
||||
const lead = members.find((m) => isLeadMember(m));
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|||
import { Badge } from '@renderer/components/ui/badge';
|
||||
import { Button } from '@renderer/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||
import { useStableTeamMentionMeta } from '@renderer/hooks/useStableTeamMentionMeta';
|
||||
import { useTeamMessagesExpanded } from '@renderer/hooks/useTeamMessagesExpanded';
|
||||
import { useTeamMessagesRead } from '@renderer/hooks/useTeamMessagesRead';
|
||||
import { useStableTeamMentionMeta } from '@renderer/hooks/useStableTeamMentionMeta';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { filterTeamMessages } from '@renderer/utils/teamMessageFiltering';
|
||||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||
|
|
@ -16,8 +16,8 @@ import {
|
|||
ChevronsDownUp,
|
||||
ChevronsUpDown,
|
||||
MessageSquare,
|
||||
PanelLeftClose,
|
||||
PanelLeft,
|
||||
PanelLeftClose,
|
||||
Search,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
|
|
@ -26,13 +26,14 @@ import { ActivityTimeline } from '../activity/ActivityTimeline';
|
|||
import { getThoughtGroupKey, groupTimelineItems } from '../activity/LeadThoughtsGroup';
|
||||
import { MessageExpandDialog } from '../activity/MessageExpandDialog';
|
||||
import { CollapsibleTeamSection } from '../CollapsibleTeamSection';
|
||||
|
||||
import { MessageComposer } from './MessageComposer';
|
||||
import { MessagesFilterPopover } from './MessagesFilterPopover';
|
||||
import { StatusBlock } from './StatusBlock';
|
||||
|
||||
import type { MessagesFilterState } from './MessagesFilterPopover';
|
||||
import type { ActionMode } from './ActionModeSelector';
|
||||
import type { TimelineItem } from '../activity/LeadThoughtsGroup';
|
||||
import type { ActionMode } from './ActionModeSelector';
|
||||
import type { MessagesFilterState } from './MessagesFilterPopover';
|
||||
import type { InboxMessage, ResolvedTeamMember, TaskRef, TeamTaskWithKanban } from '@shared/types';
|
||||
|
||||
interface TimeWindow {
|
||||
|
|
@ -631,7 +632,7 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|||
</>
|
||||
}
|
||||
defaultOpen
|
||||
action={<div className="flex items-center gap-2 pl-2 pr-2">{searchAndFilterBar}</div>}
|
||||
action={<div className="flex items-center gap-2 px-2">{searchAndFilterBar}</div>}
|
||||
>
|
||||
{messagesContent}
|
||||
</CollapsibleTeamSection>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { Check, FileCode, FileDiff, FileText, GitBranch, GitCommit, Search } from 'lucide-react';
|
||||
|
||||
/* ── Fake diff lines for the mini-terminal ─────────────────────────── */
|
||||
|
|
@ -156,10 +157,7 @@ export const ChangesLoadingAnimation = (): React.JSX.Element => {
|
|||
</svg>
|
||||
|
||||
{/* Rotating dashed orbit ring */}
|
||||
<svg
|
||||
className="clda-orbit-ring pointer-events-none absolute h-40 w-40"
|
||||
viewBox="0 0 160 160"
|
||||
>
|
||||
<svg className="clda-orbit-ring pointer-events-none absolute size-40" viewBox="0 0 160 160">
|
||||
<circle
|
||||
cx="80"
|
||||
cy="80"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export const FullDiffLoadingBanner = ({
|
|||
return (
|
||||
<div className="bg-surface/95 border-b border-border px-4 py-3">
|
||||
<div className="bg-surface-raised/80 rounded-xl border border-border shadow-sm">
|
||||
<div className="flex items-start gap-3 px-3 py-3">
|
||||
<div className="flex items-start gap-3 p-3">
|
||||
<div className="relative mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-xl border border-border bg-surface-sidebar">
|
||||
<div className="absolute inset-1 rounded-lg bg-emerald-500/10 blur-sm" />
|
||||
<LoaderCircle
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { useStore } from '@renderer/store';
|
|||
import { AlertTriangle, Clock, Loader2, Terminal } from 'lucide-react';
|
||||
|
||||
import { CliLogsRichView } from '../CliLogsRichView';
|
||||
|
||||
import { RunStatusBadge } from './ScheduleStatusBadge';
|
||||
|
||||
import type { ScheduleRun } from '@shared/types';
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
} from 'lucide-react';
|
||||
|
||||
import { LaunchTeamDialog } from '../dialogs/LaunchTeamDialog';
|
||||
|
||||
import { ScheduleEmptyState } from './ScheduleEmptyState';
|
||||
import { ScheduleRunLogDialog } from './ScheduleRunLogDialog';
|
||||
import { ScheduleRunRow } from './ScheduleRunRow';
|
||||
|
|
@ -241,7 +242,6 @@ export const ScheduleSection = ({ teamName }: ScheduleSectionProps): React.JSX.E
|
|||
try {
|
||||
await deleteSchedule(id);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to delete schedule:', err);
|
||||
}
|
||||
},
|
||||
|
|
@ -253,7 +253,6 @@ export const ScheduleSection = ({ teamName }: ScheduleSectionProps): React.JSX.E
|
|||
try {
|
||||
await triggerNow(id);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to trigger schedule:', err);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue