rolled back unintended changes not related or needed to support package manager. first phases of build pass, but fail at type checking on things that don't seem related to what I've done

This commit is contained in:
Smartsheet-JB-Brown 2025-04-16 12:33:20 -07:00
parent 1e77f62063
commit 0caf685b4f
17 changed files with 408 additions and 583 deletions

View file

@ -1,137 +0,0 @@
#!/usr/bin/env node
import { program } from "commander"
import { taskManager } from "./task-manager"
import { execSync } from "child_process"
program.name("iterate").description("CLI to manage task iterations")
program
.command("create <taskId>")
.description("Create a new iteration")
.requiredOption("-d, --description <description>", "Task description")
.action(async (taskId: string, options: { description: string }) => {
try {
await taskManager.createIteration(taskId, options.description)
console.log(`Created iteration: ${taskId}`)
} catch (error) {
console.error("Failed to create iteration:", error)
process.exit(1)
}
})
program
.command("list")
.description("List all iterations")
.action(async () => {
try {
const iterations = await taskManager.listIterations()
console.log("Available iterations:")
for (const taskId of iterations) {
const task = await taskManager.getIteration(taskId)
if (task) {
console.log(`- ${taskId}: ${task.description} (${task.current_state.status})`)
}
}
} catch (error) {
console.error("Failed to list iterations:", error)
process.exit(1)
}
})
program
.command("status <taskId>")
.description("Show iteration status")
.action(async (taskId: string) => {
try {
const task = await taskManager.getIteration(taskId)
if (!task) {
console.log("No such iteration")
return
}
console.log(`Iteration: ${task.task_id}`)
console.log(`Description: ${task.description}`)
console.log(`Status: ${task.current_state.status}`)
if (task.checkpoints.length > 0) {
console.log("\nCheckpoints:")
task.checkpoints.forEach((checkpoint, i) => {
console.log(`${i + 1}. ${checkpoint.description}`)
console.log(` Changes: ${checkpoint.changes.join(", ")}`)
console.log(` Timestamp: ${checkpoint.timestamp}`)
})
}
if (task.test_results) {
console.log("\nTest results:")
console.log(
`- Unit tests: ${task.test_results.unit_tests.passing} passing, ${task.test_results.unit_tests.failing} failing`,
)
console.log(`- Linting: ${task.test_results.linting}`)
console.log(`- Manual testing: ${task.test_results.manual_testing}`)
}
if (task.current_state.final_commit) {
console.log("\nCommit info:")
console.log(`- Hash: ${task.current_state.final_commit.hash}`)
console.log(`- Message: ${task.current_state.final_commit.message}`)
console.log("- Changes:")
task.current_state.final_commit.changes.forEach((change) => {
console.log(` * ${change}`)
})
}
} catch (error) {
console.error("Failed to get iteration status:", error)
process.exit(1)
}
})
program
.command("checkpoint <taskId>")
.description("Create a new checkpoint")
.requiredOption("-d, --description <description>", "Checkpoint description")
.requiredOption("-c, --component <component>", "Component being modified")
.requiredOption("--changes <changes...>", "List of changes")
.requiredOption("--risks <risks...>", "List of risks")
.requiredOption("--feedback <feedback...>", "Expected user feedback")
.action(async (taskId: string, options) => {
try {
const checkpoint = {
id: `checkpoint_${Date.now()}`,
description: options.description,
component: options.component,
changes: options.changes,
risks: options.risks,
expected_feedback: options.feedback,
timestamp: new Date().toISOString(),
}
await taskManager.addCheckpoint(taskId, checkpoint)
console.log(`Created checkpoint: ${checkpoint.id}`)
} catch (error) {
console.error("Failed to create checkpoint:", error)
process.exit(1)
}
})
program
.command("complete <taskId>")
.description("Complete an iteration with commit info")
.requiredOption("--message <message>", "Commit message")
.requiredOption("--changes <changes...>", "List of changes")
.action(async (taskId: string, options) => {
try {
const hash = execSync("git rev-parse HEAD").toString().trim()
await taskManager.completeIteration(taskId, {
hash,
message: options.message,
changes: options.changes,
})
console.log(`Completed iteration: ${taskId}`)
} catch (error) {
console.error("Failed to complete iteration:", error)
process.exit(1)
}
})
program.parse()

View file

@ -1,24 +0,0 @@
{
"task_id": "PM-CLEANUP-20250412",
"active_checkpoint": "",
"status": "in_progress",
"created_at": "2025-04-12T23:57:44.419Z",
"last_accessed": "2025-04-12T23:57:44.420Z",
"description": "Remove unused YamlParser implementation",
"initial_commit": "4417886324a54ad5c058813474b8a57a9859bba0",
"checkpoints": [],
"test_results": {
"unit_tests": {
"passing": 0,
"failing": 0,
"pending": 0
},
"linting": "",
"manual_testing": ""
},
"pending_decisions": [],
"rollback_info": {
"full_rollback": "git reset --hard 4417886324a54ad5c058813474b8a57a9859bba0",
"partial_rollbacks": {}
}
}

View file

@ -1,63 +0,0 @@
{
"task_id": "PM-STATE-FIX-20250412",
"active_checkpoint": "pm_state_fix_20250412_3",
"status": "in_progress",
"created_at": "2025-04-12T15:44:13-07:00",
"last_accessed": "2025-04-12T15:50:47-07:00",
"description": "Fix package manager state management and refresh issues",
"initial_commit": "4417886324a54ad5c058813474b8a57a9859bba0",
"checkpoints": [
{
"id": "pm_state_fix_20250412_1",
"commit_hash": "4417886324a54ad5c058813474b8a57a9859bba0",
"description": "UI State Management",
"component": "webview-ui/src/components/package-manager/PackageManagerView.tsx",
"changes": ["Removed premature item clearing", "Fixed state update timing"],
"risks": ["Race conditions between state updates", "Stale data display during refresh"],
"expected_feedback": [
"Items disappear and reappear during refresh",
"Refresh button gets stuck spinning",
"Old items shown after source changes"
],
"timestamp": "2025-04-12T15:44:13-07:00"
},
{
"id": "pm_state_fix_20250412_2",
"commit_hash": "4417886324a54ad5c058813474b8a57a9859bba0",
"description": "Error Handling",
"component": "webview-ui/src/components/package-manager/PackageManagerView.tsx",
"changes": ["Removed client-side error messages", "Improved timeout handling"],
"risks": ["Missing error feedback", "Timeout state confusion"],
"expected_feedback": ["No error message shown on failure", "UI stuck in loading state"],
"timestamp": "2025-04-12T15:45:00-07:00"
},
{
"id": "pm_state_fix_20250412_3",
"commit_hash": "4417886324a54ad5c058813474b8a57a9859bba0",
"description": "State Reset Logic",
"component": "webview-ui/src/components/package-manager/PackageManagerView.tsx",
"changes": ["Always update items on state change", "Proper timeout cleanup"],
"risks": ["Memory leaks from timeouts", "Inconsistent state after tab switch"],
"expected_feedback": ["Items don't update after source changes", "Refresh button state incorrect"],
"timestamp": "2025-04-12T15:45:30-07:00"
}
],
"test_results": {
"unit_tests": {
"passing": 1263,
"failing": 0,
"pending": 23
},
"linting": "No errors",
"manual_testing": "Confirmed working by user"
},
"pending_decisions": [],
"rollback_info": {
"full_rollback": "git reset --hard 4417886324a54ad5c058813474b8a57a9859bba0",
"partial_rollbacks": {
"ui_state": "git checkout pm_state_fix_20250412_1",
"error_handling": "git checkout pm_state_fix_20250412_2",
"state_reset": "git checkout pm_state_fix_20250412_3"
}
}
}

View file

@ -1,84 +0,0 @@
{
"task_id": "4d3f7a2e-8c1b-4f9d-b5e2-9c1d8f3a6b4d",
"description": "Rename prepare-for-commit routine to iterate",
"created_at": "2025-04-12T17:32:55-07:00",
"checkpoints": [
{
"id": "checkpoint_1",
"description": "Rename files and directories",
"changes": [
"Renamed prepare_logs to iterations",
"Renamed prepare-cli.ts to iterate-cli.ts",
"Updated package.json with new names"
],
"risks": [
"Breaking existing task logs",
"Path references might be incorrect",
"Package dependencies might need updates"
],
"expected_feedback": [
"CLI commands not working",
"Missing or inaccessible logs",
"Build errors from renamed paths"
],
"timestamp": "2025-04-12T17:33:01-07:00"
},
{
"id": "checkpoint_2",
"description": "Update task manager implementation",
"changes": [
"Renamed methods to use 'iteration' terminology",
"Improved TypeScript types",
"Added better error handling",
"Simplified file operations"
],
"risks": [
"Type mismatches with existing code",
"Regression in error handling",
"Data format inconsistencies"
],
"expected_feedback": [
"Type errors in TypeScript",
"Unexpected error messages",
"Missing or incorrect data in logs"
],
"timestamp": "2025-04-12T17:33:28-07:00"
},
{
"id": "checkpoint_3",
"description": "Update CLI interface",
"changes": [
"Renamed CLI commands to use new terminology",
"Improved command structure",
"Added better error messages",
"Updated command documentation"
],
"risks": [
"Breaking existing scripts",
"Confusing user experience during transition",
"Missing command functionality"
],
"expected_feedback": [
"CLI commands not recognized",
"Unclear error messages",
"Missing features from old interface"
],
"timestamp": "2025-04-12T17:33:52-07:00"
}
],
"current_state": {
"status": "completed",
"summary": "Successfully renamed prepare-for-commit routine to iterate with improved implementation",
"final_commit": {
"hash": "61c9480b",
"message": "refactor: rename prepare-for-commit to iterate",
"changes": [
"Renamed prepare_logs to iterations",
"Updated task manager to use new terminology",
"Simplified CLI interface",
"Added better TypeScript types",
"Improved error handling"
]
}
}
}

View file

@ -1,94 +0,0 @@
{
"task_id": "e2e_analysis_20250412",
"description": "Analyze value of package manager e2e tests vs unit tests",
"created_at": "2025-04-12T17:37:45-07:00",
"checkpoints": [
{
"id": "checkpoint_1",
"description": "Analysis of test coverage and complexity",
"component": "e2e/src/suite/package-manager.test.ts, src/__mocks__/vscode.js",
"findings": [
{
"category": "Unit Test Coverage",
"details": [
"GitFetcher tests - handles repository cloning and updates",
"MetadataScanner tests - validates component discovery",
"RepositoryStructureValidation tests - ensures correct file structure",
"Schema validation tests - verifies metadata format",
"ParsePackageManagerItems tests - checks item parsing logic",
"GitCommandQuoting tests - ensures safe command handling"
]
},
{
"category": "E2E Test Coverage",
"details": [
"Real cache location testing",
"Package metadata with external items",
"Optional fields handling",
"Invalid source handling",
"Missing metadata handling",
"Localized metadata support"
]
}
]
},
{
"id": "checkpoint_2",
"description": "Implementation of enhanced unit tests",
"component": "src/services/package-manager/__tests__/enhanced/*.test.ts",
"changes": [
{
"file": "GitFetcher.test.ts",
"improvements": [
"Added proper VSCode extension context mocking",
"Enhanced cache directory testing",
"Added network error handling tests",
"Added rate limiting tests"
]
},
{
"file": "MetadataScanner.test.ts",
"improvements": [
"Added comprehensive localization testing",
"Enhanced external items validation",
"Added proper TypeScript types",
"Improved error case coverage"
]
},
{
"file": "RepositoryStructureValidation.test.ts",
"improvements": [
"Added proper fs.Stats mocking",
"Enhanced directory structure validation",
"Added security validation tests",
"Improved error handling coverage"
]
}
]
},
{
"id": "checkpoint_3",
"description": "Implementation of changes",
"completed_at": "2025-04-12T17:48:01-07:00",
"changes_made": [
"Removed e2e/src/suite/package-manager.test.ts",
"Reverted src/__mocks__/vscode.js to simpler version",
"Added enhanced unit test files with proper TypeScript support",
"Fixed all TypeScript errors in new tests"
],
"commit": {
"hash": "7a62bc50",
"message": "refactor: remove package manager e2e tests in favor of enhanced unit tests",
"stats": {
"files_changed": 7,
"insertions": 496,
"deletions": 466
}
}
}
],
"current_state": {
"status": "completed",
"summary": "Successfully replaced e2e tests with enhanced unit tests that provide better coverage, improved maintainability, and reduced complexity. The new tests cover all previous e2e scenarios while adding better error handling, proper TypeScript support, and comprehensive validation of edge cases."
}
}

40
.roomodes Normal file

File diff suppressed because one or more lines are too long

View file

@ -173,7 +173,6 @@ const extensionConfig = {
{
name: "alias-plugin",
setup(build) {
// Handle pkce-challenge alias
build.onResolve({ filter: /^pkce-challenge$/ }, (args) => {
return { path: require.resolve("pkce-challenge/dist/index.browser.js") }
})

View file

@ -0,0 +1,210 @@
// Mock file system data
const mockFiles = new Map()
const mockDirectories = new Set()
// Initialize base test directories
const baseTestDirs = [
"/mock",
"/mock/extension",
"/mock/extension/path",
"/mock/storage",
"/mock/storage/path",
"/mock/settings",
"/mock/settings/path",
"/mock/mcp",
"/mock/mcp/path",
"/test",
"/test/path",
"/test/storage",
"/test/storage/path",
"/test/storage/path/settings",
"/test/extension",
"/test/extension/path",
"/test/global-storage",
"/test/log/path",
]
// Helper function to format instructions
const formatInstructions = (sections: string[]): string => {
const joinedSections = sections.filter(Boolean).join("\n\n")
return joinedSections
? `
====
USER'S CUSTOM INSTRUCTIONS
The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
${joinedSections}`
: ""
}
// Helper function to format rule content
const formatRuleContent = (ruleFile: string, content: string): string => {
return `Rules:\n# Rules from ${ruleFile}:\n${content}`
}
type RuleFiles = {
".clinerules-code": string
".clinerules-ask": string
".clinerules-architect": string
".clinerules-test": string
".clinerules-review": string
".clinerules": string
}
// Helper function to ensure directory exists
const ensureDirectoryExists = (path: string) => {
const parts = path.split("/")
let currentPath = ""
for (const part of parts) {
if (!part) continue
currentPath += "/" + part
mockDirectories.add(currentPath)
}
}
const mockFs = {
readFile: jest.fn().mockImplementation(async (filePath: string, encoding?: string) => {
// Return stored content if it exists
if (mockFiles.has(filePath)) {
return mockFiles.get(filePath)
}
// Handle rule files
const ruleFiles: RuleFiles = {
".clinerules-code": "# Code Mode Rules\n1. Code specific rule",
".clinerules-ask": "# Ask Mode Rules\n1. Ask specific rule",
".clinerules-architect": "# Architect Mode Rules\n1. Architect specific rule",
".clinerules-test":
"# Test Engineer Rules\n1. Always write tests first\n2. Get approval before modifying non-test code",
".clinerules-review":
"# Code Reviewer Rules\n1. Provide specific examples in feedback\n2. Focus on maintainability and best practices",
".clinerules": "# Test Rules\n1. First rule\n2. Second rule",
}
// Check for exact file name match
const fileName = filePath.split("/").pop()
if (fileName && fileName in ruleFiles) {
return ruleFiles[fileName as keyof RuleFiles]
}
// Check for file name in path
for (const [ruleFile, content] of Object.entries(ruleFiles)) {
if (filePath.includes(ruleFile)) {
return content
}
}
// Handle file not found
const error = new Error(`ENOENT: no such file or directory, open '${filePath}'`)
;(error as any).code = "ENOENT"
throw error
}),
writeFile: jest.fn().mockImplementation(async (path: string, content: string) => {
// Ensure parent directory exists
const parentDir = path.split("/").slice(0, -1).join("/")
ensureDirectoryExists(parentDir)
mockFiles.set(path, content)
return Promise.resolve()
}),
mkdir: jest.fn().mockImplementation(async (path: string, options?: { recursive?: boolean }) => {
// Always handle recursive creation
const parts = path.split("/")
let currentPath = ""
// For recursive or test/mock paths, create all parent directories
if (options?.recursive || path.startsWith("/test") || path.startsWith("/mock")) {
for (const part of parts) {
if (!part) continue
currentPath += "/" + part
mockDirectories.add(currentPath)
}
return Promise.resolve()
}
// For non-recursive paths, verify parent exists
for (let i = 0; i < parts.length - 1; i++) {
if (!parts[i]) continue
currentPath += "/" + parts[i]
if (!mockDirectories.has(currentPath)) {
const error = new Error(`ENOENT: no such file or directory, mkdir '${path}'`)
;(error as any).code = "ENOENT"
throw error
}
}
// Add the final directory
currentPath += "/" + parts[parts.length - 1]
mockDirectories.add(currentPath)
return Promise.resolve()
}),
access: jest.fn().mockImplementation(async (path: string) => {
// Check if the path exists in either files or directories
if (mockFiles.has(path) || mockDirectories.has(path) || path.startsWith("/test")) {
return Promise.resolve()
}
const error = new Error(`ENOENT: no such file or directory, access '${path}'`)
;(error as any).code = "ENOENT"
throw error
}),
rename: jest.fn().mockImplementation(async (oldPath: string, newPath: string) => {
// Check if the old file exists
if (mockFiles.has(oldPath)) {
// Copy content to new path
const content = mockFiles.get(oldPath)
mockFiles.set(newPath, content)
// Delete old file
mockFiles.delete(oldPath)
return Promise.resolve()
}
// If old file doesn't exist, throw an error
const error = new Error(`ENOENT: no such file or directory, rename '${oldPath}'`)
;(error as any).code = "ENOENT"
throw error
}),
constants: jest.requireActual("fs").constants,
// Expose mock data for test assertions
_mockFiles: mockFiles,
_mockDirectories: mockDirectories,
// Helper to set up initial mock data
_setInitialMockData: () => {
// Set up default MCP settings
mockFiles.set(
"/mock/settings/path/mcp_settings.json",
JSON.stringify({
mcpServers: {
"test-server": {
command: "node",
args: ["test.js"],
disabled: false,
alwaysAllow: ["existing-tool"],
},
},
}),
)
// Ensure all base directories exist
baseTestDirs.forEach((dir) => {
const parts = dir.split("/")
let currentPath = ""
for (const part of parts) {
if (!part) continue
currentPath += "/" + part
mockDirectories.add(currentPath)
}
})
},
}
// Initialize mock data
mockFs._setInitialMockData()
module.exports = mockFs

View file

@ -476,50 +476,167 @@ describe("Cline", () => {
})
it("should handle image blocks based on model capabilities", async () => {
// Create a single test instance with image support
const [cline] = Cline.create({
provider: mockProvider,
apiConfiguration: {
...mockApiConfig,
apiModelId: "claude-3-sonnet",
},
task: "test task",
})
// Create two configurations - one with image support, one without
const configWithImages = {
...mockApiConfig,
apiModelId: "claude-3-sonnet",
}
const configWithoutImages = {
...mockApiConfig,
apiModelId: "gpt-3.5-turbo",
}
// Mock image support
jest.spyOn(cline.api, "getModel").mockReturnValue({
id: "claude-3-sonnet",
info: { supportsImages: true } as ModelInfo,
})
// Set up simple conversation history
cline.apiConversationHistory = [
// Create test conversation history with mixed content
const conversationHistory: (Anthropic.MessageParam & { ts?: number })[] = [
{
role: "user",
role: "user" as const,
content: [
{ type: "text", text: "Here is an image" },
{ type: "image", source: { type: "base64", media_type: "image/jpeg", data: "test" } },
{
type: "text" as const,
text: "Here is an image",
} satisfies Anthropic.TextBlockParam,
{
type: "image" as const,
source: {
type: "base64" as const,
media_type: "image/jpeg",
data: "base64data",
},
} satisfies Anthropic.ImageBlockParam,
],
},
{
role: "assistant" as const,
content: [
{
type: "text" as const,
text: "I see the image",
} satisfies Anthropic.TextBlockParam,
],
},
]
// Mock createMessage
const createMessageSpy = jest.fn().mockReturnValue(
(async function* () {
yield { type: "text", text: "response" }
})(),
)
jest.spyOn(cline.api, "createMessage").mockImplementation(createMessageSpy)
// Test with model that supports images
const [clineWithImages, taskWithImages] = Cline.create({
provider: mockProvider,
apiConfiguration: configWithImages,
task: "test task",
})
// Trigger request
await cline.recursivelyMakeClineRequests([{ type: "text", text: "test" }])
// Mock the model info to indicate image support
jest.spyOn(clineWithImages.api, "getModel").mockReturnValue({
id: "claude-3-sonnet",
info: {
supportsImages: true,
supportsPromptCache: true,
supportsComputerUse: true,
contextWindow: 200000,
maxTokens: 4096,
inputPrice: 0.25,
outputPrice: 0.75,
} as ModelInfo,
})
// Verify image block was preserved
const calls = createMessageSpy.mock.calls
expect(calls[0][1][0].content[1]).toHaveProperty("type", "image")
clineWithImages.apiConversationHistory = conversationHistory
// Clean up
await cline.abortTask(true)
// Test with model that doesn't support images
const [clineWithoutImages, taskWithoutImages] = Cline.create({
provider: mockProvider,
apiConfiguration: configWithoutImages,
task: "test task",
})
// Mock the model info to indicate no image support
jest.spyOn(clineWithoutImages.api, "getModel").mockReturnValue({
id: "gpt-3.5-turbo",
info: {
supportsImages: false,
supportsPromptCache: false,
supportsComputerUse: false,
contextWindow: 16000,
maxTokens: 2048,
inputPrice: 0.1,
outputPrice: 0.2,
} as ModelInfo,
})
clineWithoutImages.apiConversationHistory = conversationHistory
// Mock abort state for both instances
Object.defineProperty(clineWithImages, "abort", {
get: () => false,
set: () => {},
configurable: true,
})
Object.defineProperty(clineWithoutImages, "abort", {
get: () => false,
set: () => {},
configurable: true,
})
// Mock environment details and context loading
jest.spyOn(clineWithImages as any, "getEnvironmentDetails").mockResolvedValue("")
jest.spyOn(clineWithoutImages as any, "getEnvironmentDetails").mockResolvedValue("")
jest.spyOn(clineWithImages as any, "loadContext").mockImplementation(async (content) => [content, ""])
jest.spyOn(clineWithoutImages as any, "loadContext").mockImplementation(async (content) => [
content,
"",
])
// Set up mock streams
const mockStreamWithImages = (async function* () {
yield { type: "text", text: "test response" }
})()
const mockStreamWithoutImages = (async function* () {
yield { type: "text", text: "test response" }
})()
// Set up spies
const imagesSpy = jest.fn().mockReturnValue(mockStreamWithImages)
const noImagesSpy = jest.fn().mockReturnValue(mockStreamWithoutImages)
jest.spyOn(clineWithImages.api, "createMessage").mockImplementation(imagesSpy)
jest.spyOn(clineWithoutImages.api, "createMessage").mockImplementation(noImagesSpy)
// Set up conversation history with images
clineWithImages.apiConversationHistory = [
{
role: "user",
content: [
{ type: "text", text: "Here is an image" },
{ type: "image", source: { type: "base64", media_type: "image/jpeg", data: "base64data" } },
],
},
]
clineWithImages.abandoned = true
await taskWithImages.catch(() => {})
clineWithoutImages.abandoned = true
await taskWithoutImages.catch(() => {})
// Trigger API requests
await clineWithImages.recursivelyMakeClineRequests([{ type: "text", text: "test request" }])
await clineWithoutImages.recursivelyMakeClineRequests([{ type: "text", text: "test request" }])
// Get the calls
const imagesCalls = imagesSpy.mock.calls
const noImagesCalls = noImagesSpy.mock.calls
// Verify model with image support preserves image blocks
expect(imagesCalls[0][1][0].content).toHaveLength(2)
expect(imagesCalls[0][1][0].content[0]).toEqual({ type: "text", text: "Here is an image" })
expect(imagesCalls[0][1][0].content[1]).toHaveProperty("type", "image")
// Verify model without image support converts image blocks to text
expect(noImagesCalls[0][1][0].content).toHaveLength(2)
expect(noImagesCalls[0][1][0].content[0]).toEqual({ type: "text", text: "Here is an image" })
expect(noImagesCalls[0][1][0].content[1]).toEqual({
type: "text",
text: "[Referenced image in conversation]",
})
})
it.skip("should handle API retry with countdown", async () => {

View file

@ -24,7 +24,7 @@ if (!isTestEnv) {
const fs = require("fs")
const path = require("path")
const localesDir = path.join(__dirname, "locales")
const localesDir = path.join(__dirname, "i18n", "locales")
try {
// Find all language directories

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"version":3,"file":"git.js","sourceRoot":"","sources":["git.ts"],"names":[],"mappings":"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"}

View file

@ -6,7 +6,7 @@ import { groupItemsByType, GroupedItems } from "../utils/grouping"
import { ExpandableSection } from "./ExpandableSection"
import { TypeGroup } from "./TypeGroup"
import { ViewState } from "../PackageManagerViewStateManager"
import { t } from "@/i18n"
import { useAppTranslation } from "@/i18n/TranslationContext"
interface PackageManagerItemCardProps {
item: PackageManagerItem
@ -23,6 +23,7 @@ export const PackageManagerItemCard: React.FC<PackageManagerItemCardProps> = ({
activeTab,
setActiveTab,
}) => {
const { t } = useAppTranslation()
const isValidUrl = (urlString: string): boolean => {
try {
new URL(urlString)

View file

@ -1,6 +1,6 @@
import React from "react"
import { cn } from "@/lib/utils"
import { t } from "@/i18n"
import { useAppTranslation } from "@/i18n/TranslationContext"
interface TypeGroupProps {
type: string
@ -18,6 +18,7 @@ interface TypeGroupProps {
}
export const TypeGroup: React.FC<TypeGroupProps> = ({ type, items, className }) => {
const { t } = useAppTranslation()
const getTypeLabel = (type: string) => {
switch (type) {
case "mode":

View file

@ -1,117 +0,0 @@
import { useCallback, useReducer } from "react"
import { PackageManagerItem, PackageManagerSource } from "@services/package-manager"
import { PackageManagerViewStateManager } from "./PackageManagerViewStateManager"
interface State {
allItems: PackageManagerItem[]
displayItems: PackageManagerItem[]
isFetching: boolean
activeTab: "browse" | "sources"
filters: {
search: string
type: string
tags: string[]
}
sortConfig: {
by: "name" | "lastUpdated"
order: "asc" | "desc"
}
sources: PackageManagerSource[]
refreshingUrls: string[]
}
type Action =
| { type: "FETCH_ITEMS" }
| { type: "SET_ACTIVE_TAB"; payload: { tab: "browse" | "sources" } }
| { type: "UPDATE_FILTERS"; payload: { filters: Partial<State["filters"]> } }
| { type: "UPDATE_SORT"; payload: { sortConfig: Partial<State["sortConfig"]> } }
| { type: "UPDATE_SOURCES"; payload: { sources: PackageManagerSource[] } }
| { type: "REFRESH_SOURCE"; payload: { url: string } }
const initialState: State = {
allItems: [],
displayItems: [],
isFetching: false,
activeTab: "browse",
filters: {
search: "",
type: "",
tags: [],
},
sortConfig: {
by: "name",
order: "asc",
},
sources: [],
refreshingUrls: [],
}
const stateManager = new PackageManagerViewStateManager()
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FETCH_ITEMS":
return {
...state,
isFetching: true,
}
case "SET_ACTIVE_TAB":
return {
...state,
activeTab: action.payload.tab,
}
case "UPDATE_FILTERS":
const newFilters = {
...state.filters,
...action.payload.filters,
}
stateManager.setItems(state.allItems)
stateManager.setFilters(newFilters)
return {
...state,
filters: newFilters,
displayItems: stateManager.getFilteredAndSortedItems(),
}
case "UPDATE_SORT":
const newSortConfig = {
...state.sortConfig,
...action.payload.sortConfig,
}
stateManager.setSortBy(newSortConfig.by)
stateManager.setSortOrder(newSortConfig.order)
stateManager.setItems(state.allItems)
return {
...state,
sortConfig: newSortConfig,
displayItems: stateManager.getFilteredAndSortedItems(),
}
case "UPDATE_SOURCES":
return {
...state,
sources: action.payload.sources,
}
case "REFRESH_SOURCE":
return {
...state,
refreshingUrls: [...state.refreshingUrls, action.payload.url],
}
default:
return state
}
}
export function useStateManager() {
const [state, dispatch] = useReducer(reducer, initialState)
const transition = useCallback((action: Action) => {
dispatch(action)
}, [])
return [state, { transition }] as const
}

View file

@ -1,20 +0,0 @@
import i18next from "i18next"
import { initReactI18next } from "react-i18next"
import packageManagerEn from "../../src/i18n/locales/en/package_manager.json"
// Initialize i18next
i18next.use(initReactI18next).init({
resources: {
en: {
package_manager: packageManagerEn,
},
},
lng: "en",
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
})
export const t = i18next.t
export default i18next

View file

@ -1,13 +1,13 @@
import { mentionRegex } from "../../../src/shared/context-mentions"
import { Fzf } from "fzf"
import { ModeConfig } from "../../../src/shared/modes"
import * as path from "path"
export interface SearchResult {
path: string
type: "file" | "folder"
label?: string
}
export function insertMention(
text: string,
position: number,
@ -231,13 +231,11 @@ export function getContextMenuOptions(
// Convert search results to queryItems format
const searchResultItems = dynamicSearchResults.map((result) => {
const formattedPath = result.path.startsWith("/") ? result.path : `/${result.path}`
const pathParts = formattedPath.split("/")
const fileName = pathParts[pathParts.length - 1]
return {
type: result.type === "folder" ? ContextMenuOptionType.Folder : ContextMenuOptionType.File,
value: formattedPath,
label: result.label || fileName,
label: result.label || path.basename(result.path),
description: formattedPath,
}
})