Skip to content

feat: add vscode extension#261

Merged
luxass merged 6 commits intomainfrom
vscode-v2
Sep 27, 2025
Merged

feat: add vscode extension#261
luxass merged 6 commits intomainfrom
vscode-v2

Conversation

@luxass
Copy link
Copy Markdown
Member

@luxass luxass commented Sep 27, 2025

🔗 Linked issue

📚 Description

This is moved from https://github.com/ucdjs/vscode-ucd to have more freedom when changing code.

Summary by CodeRabbit

  • New Features

    • Introduces a VS Code extension with a UCD Explorer tree view to browse Unicode data by version and open files directly in the editor.
    • Adds UCD virtual document support for fetching and displaying content.
    • Registers the “ucd” language and commands to browse files, open entries, and refresh the explorer.
  • Documentation

    • Adds a README describing commands, configuration options, views, and language support.
  • Chores

    • Adds workspace and tooling setup for the new extension, including recommended extensions, tasks, launch configuration, TypeScript configs, and build settings.

* Introduced `launch.json` for debugging configuration.
* Added `tasks.json` to define npm tasks for background execution.
…or package.json

* Added "amodio.tsl-problem-matcher" to the recommended extensions.
* Introduced a new command for `vscode/package.json` to run `pnpm --filter ./vscode run ext:gen`.
* Added `reactive-vscode` and `@reactive-vscode/vueuse` to the vscode catalog.
* Updated existing vscode dependencies for better compatibility.
…ovider

- Introduced a new VSCode extension for visualizing and managing UCD files.
- Added commands for browsing UCD files, visualizing files, refreshing the explorer, and opening entries.
- Implemented a content provider to fetch and display UCD file content.
- Created a tree view for navigating UCD versions and files.
- Integrated configuration options for local data file paths and API URLs.
- Set up ESLint configuration and TypeScript build configurations.
- Established a logger for better debugging and error handling.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Sep 27, 2025

⚠️ No Changeset found

Latest commit: d45a8a8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 27, 2025

Walkthrough

Adds a new VS Code extension workspace under vscode/, including configs, build tooling, and extension scaffolding. Implements activation, a UCD explorer view with lazy-loading, a content provider for ucd: URIs, command registrations (browse, refresh, open, visualize placeholder), and a VS Code FS bridge. Updates root workspace to include vscode.

Changes

Cohort / File(s) Summary
VS Code workspace configs
/.vscode/extensions.json, /.vscode/launch.json, /.vscode/settings.json, /.vscode/tasks.json
Adds recommended extension amodio.tsl-problem-matcher; introduces launch config for Extension Host; adds run-on-save command for generating VSCode artifacts; adds background build task for dev:vscode with ts-webpack-watch matcher.
Monorepo workspace updates
/package.json, /pnpm-workspace.yaml
Includes vscode in workspaces; extends pnpm workspace with vscode package, types (@types/vscode), and tool versions (vscode-ext-gen, reactive-vscode, @reactive-vscode/vueuse, @vscode/vsce).
VS Code package metadata & tooling
/vscode/package.json, /vscode/README.md, /vscode/.gitignore, /vscode/eslint.config.js, /vscode/tsconfig.json, /vscode/tsconfig.build.json, /vscode/tsdown.config.ts, /vscode/turbo.json
Adds extension metadata, contributions, scripts; README documenting commands/config; ignores src/generated; ESLint config; TS configs; tsdown bundling config; Turbo tasks (build, ext:gen, dev, typecheck).
Extension entry & view
/vscode/src/extension.ts, /vscode/src/views/ucd-explorer.ts
Implements activation/deactivation; initializes UCD explorer TreeView, handles expand to load children lazily.
Commands
/vscode/src/commands/index.ts, /vscode/src/commands/browse-ucd-files.ts, /vscode/src/commands/refresh-explorer.ts, /vscode/src/commands/open-entry.ts, /vscode/src/commands/visualize-file.ts
Registers commands; browse focuses explorer; refresh triggers explorer refresh; open-entry opens either external URL or ucd: document; visualize-file shows not-implemented message.
Composables (state/config)
/vscode/src/composables/useUCDExplorer.ts, /vscode/src/composables/useUCDStore.ts, /vscode/src/composables/useUCDContentProvider.ts, /vscode/src/config.ts
Explorer state with caching and loaders; store factory switching between HTTP/local FS and watching config; registers ucd content provider; defines scoped config object.
Libs & utilities
/vscode/src/lib/files.ts, /vscode/src/lib/vscode-fs-bridge.ts, /vscode/src/lib/ucd-content-provider.ts, /vscode/src/logger.ts
Maps UCD file tree to TreeView nodes; VS Code FS bridge (read/write/listdir/etc.); ucd content provider fetching content; namespaced logger.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor VSCode
  participant Ext as Extension
  participant View as UCD Explorer View
  participant Store as UCD Store
  participant CP as UCD Content Provider

  VSCode->>Ext: activate()
  Ext->>View: initializeUCDExplorerView()
  Ext->>Ext: registerCommands()
  Ext->>CP: registerTextDocumentContentProvider("ucd")
  Ext-->>VSCode: activation complete

  note over View: User expands a version folder
  VSCode->>View: onDidExpandElement(version)
  View->>Store: loadChildrenForVersion(version)
  Store-->>View: TreeViewNode[]
  View-->>VSCode: render children
Loading
sequenceDiagram
  autonumber
  actor User
  participant VSCode
  participant Cmd as openEntry Command
  participant CP as UCD Content Provider

  User->>VSCode: Execute "ucd.openEntry"
  VSCode->>Cmd: invoke(version/file or tree item)
  alt Tree item with URL
    Cmd->>VSCode: executeCommand("vscode.open", Uri)
  else Version + file path
    Cmd->>VSCode: openTextDocument(ucd:<version>/<path>)
    VSCode->>CP: provideTextDocumentContent(uri)
    CP-->>VSCode: fetch text or error string
    VSCode->>VSCode: showTextDocument + set language "ucd"
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • feat: add ucd-store #25 — Introduces ucd-store APIs used here (createUCDStore/createHTTPUCDStore, types), directly enabling the store/composables and file-tree features in the extension.

Poem

A rabbit taps keys by the moon’s soft glow,
Growing a burrow where UCD files flow.
Click, expand—trees rustle with lore,
ucd: paths open like tunnels galore.
Not yet visualize? That’s a carrot-to-be—
For now, hop in the Explorer with glee. 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “feat: add vscode extension” accurately and concisely describes the primary change of introducing the VS Code extension into the repository, aligning with the extensive addition of configuration, code, and documentation for the extension. It follows conventional commit style without extraneous details and clearly conveys the main purpose to any reviewer scanning the history. There is no misleading or generic wording.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch vscode-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 27, 2025

📋 OpenAPI Schema Analysis

No changes detected - The OpenAPI schema is identical to the main branch.

🤖 This comment is automatically updated when you push new commits.

…ndencies

* Added a README.md file detailing commands, configurations, and languages for the vscode-ucd extension.
* Updated turbo.json to ensure `typecheck` task depends on `ext:gen`.
@github-actions
Copy link
Copy Markdown
Contributor

Preview Deployment for Web

The Web worker has been deployed successfully.

Preview URL: https://preview.ucdjs.dev

This preview was built from commit d45a8a8


🤖 This comment will be updated automatically when you push new commits to this PR.

@github-actions
Copy link
Copy Markdown
Contributor

Preview Deployment for Api

The Api worker has been deployed successfully.

Preview URL: https://preview.api.ucdjs.dev

This preview was built from commit d45a8a8


🤖 This comment will be updated automatically when you push new commits to this PR.

@luxass luxass marked this pull request as ready for review September 27, 2025 13:51
@luxass luxass merged commit a8888de into main Sep 27, 2025
17 of 20 checks passed
@luxass luxass deleted the vscode-v2 branch September 27, 2025 13:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
package.json (1)

5-6: Update Node.js engines requirement.

The Node.js engines requirement should be >= 22.17 according to the coding guidelines for the root package.json.

-    "node": ">=22.18"
+    "node": ">=22.17"

As per coding guidelines

🧹 Nitpick comments (13)
vscode/src/commands/index.ts (1)

6-11: Guard against duplicate command registration

If called twice, command registration may throw (“command already exists”). Add an idempotency guard.

Based on learnings

 import { useVisualizeFileCommand } from "./visualize-file";
 
+let registered = false;
+
 export function registerCommands() {
+  if (registered) return;
+  registered = true;
   useBrowseUcdFilesCommand();
   useRefreshExplorerCommand();
   useVisualizeFileCommand();
   useOpenEntryCommand();
 }

Ensure activation calls this exactly once.

vscode/src/commands/visualize-file.ts (1)

1-9: LGTM! Well-structured command stub with appropriate placeholder.

The implementation follows consistent patterns with other commands in the codebase and correctly uses reactive-vscode for command registration. The error message clearly indicates the incomplete state.

Since this is a placeholder implementation, would you like me to help generate a working visualization command that could:

  • Parse UCD file content
  • Display it in a webview panel
  • Handle different UCD file types appropriately

Based on learnings about reactive-vscode patterns and VS Code extension development.

vscode/src/views/ucd-explorer.ts (1)

16-18: Handle thenable treeItem correctly.

The logic correctly handles both synchronous TreeItem objects and thenable promises, but the type checking could be more robust.

Consider a more precise type guard:

-      const treeItem = typeof element.treeItem === "object" && "then" in element.treeItem
-        ? await element.treeItem
-        : element.treeItem;
+      const treeItem = element.treeItem && typeof element.treeItem === "object" && "then" in element.treeItem
+        ? await element.treeItem
+        : element.treeItem;
vscode/package.json (1)

24-26: Validate minimum VS Code engine version

Confirm the extension uses APIs introduced in VS Code 1.100 (released May 8 2025). Otherwise, lower the "vscode" engine in package.json to ^1.99.0 to avoid excluding users still on 1.99.x.

vscode/src/composables/useUCDStore.ts (3)

33-34: Use logger consistently instead of console.error.

Replace console.error with logger.error for consistent logging and easier tracing.

Apply this diff:

-      console.error("Failed to initialize UCD store:", error);
+      logger.error("Failed to initialize UCD store:", error as Error);
-        console.error("Failed to create UCD store:", error);
+        logger.error("Failed to create UCD store:", error as Error);

Also applies to: 49-51


12-14: Avoid JSON.stringify in logs; pass structured data.

Prefer structured logging to keep objects intact.

Example:

-    logger.info("Creating UCD store with config:", JSON.stringify({ localDataFilesStore, globalFilters }));
+    logger.info("Creating UCD store with config", { localDataFilesStore, globalFilters });

If the logger doesn’t support objects, keep as-is. [Based on learnings]


39-54: Recreate store when global filters change.

Currently only local-store-path triggers recreation. Include store-filters to reflect filter changes.

Example update:

watch(
  [() => config["local-store-path"], () => config["store-filters"]],
  async ([newPath, _newFilters], [oldPath, _oldFilters]) => {
    if (newPath === oldPath) return;
    // creation logic as above (with seq guard)
  },
  { immediate: true },
);
vscode/src/commands/open-entry.ts (2)

45-50: Compose URIs safely and handle open failures.

Use Uri.from to avoid encoding issues and wrap in try/catch to surface errors.

Apply this diff:

-    const textEditor = await window.showTextDocument(Uri.parse(`ucd:${version}/${hasUCDFolderPath(version) ? "ucd/" : ""}${filePath}`));
-    // I can't figure out how to set the language of the text editor directly,
-    // so we use the languages API to set the language for the document.
-    // The only issue is that, this triggers a close event, and a open event...
-    await languages.setTextDocumentLanguage(textEditor.document, "ucd");
+    try {
+      const path = `/${version}/${hasUCDFolderPath(version) ? "ucd/" : ""}${filePath}`;
+      const uri = Uri.from({ scheme: "ucd", path });
+      const textEditor = await window.showTextDocument(uri);
+      // Setting the language triggers a reopen; acceptable trade-off for now.
+      await languages.setTextDocumentLanguage(textEditor.document, "ucd");
+    } catch (err) {
+      logger.error("Failed to open UCD entry", err as Error);
+    }

16-37: Tighten tree-item checks and remove redundancy.

Single guard on __ucd and url is enough.

Apply this diff:

-    if (typeof versionOrTreeView === "object" && "treeItem" in versionOrTreeView) {
+    if (typeof versionOrTreeView === "object" && "treeItem" in versionOrTreeView) {
       const treeView = versionOrTreeView;
-
-      if (!treeView.treeItem || !(treeView.treeItem as UCDTreeItem).__ucd) {
-        logger.error("Invalid entry provided to openEntry command.");
-        return;
-      }
-
-      const ucdItem = (treeView.treeItem as UCDTreeItem).__ucd;
-      if (!ucdItem) {
-        logger.error("UCD item is undefined or null.");
-        return;
-      }
-
-      if (!ucdItem?.url) {
-        logger.error("UCD item does not have a valid URL.");
-        return;
-      }
-
-      executeCommand("vscode.open", Uri.parse(ucdItem.url));
+      const ucdItem = (treeView.treeItem as UCDTreeItem | undefined)?.__ucd;
+      if (!ucdItem?.url) {
+        logger.error("Invalid UCD tree item provided to openEntry command.");
+        return;
+      }
+      await executeCommand("vscode.open", Uri.parse(ucdItem.url));
       return;
     }
vscode/src/lib/vscode-fs-bridge.ts (4)

4-4: Avoid magic numbers; use FileType enum.

Replace 2 with FileType.Directory and import FileType.

Apply this diff:

-import { Uri, workspace } from "vscode";
+import { Uri, workspace, FileType } from "vscode";
-          const isDirectory = type === 2; // FileType.Directory
+          const isDirectory = type === FileType.Directory;
-              if (type === 2) { // Directory
+              if (type === FileType.Directory) { // Directory

Also applies to: 36-36, 71-71


114-121: Resolve parent directory robustly (avoid appending “..”).

Compute parent with path.dirname to avoid edge cases.

Apply this diff:

-        const parentUri = Uri.joinPath(resolvedUri, "..");
+        const parentUri = Uri.file(path.dirname(resolvedUri.fsPath));

And add this import at the top of the file:

import * as path from "node:path";

19-20: Be explicit about encoding.

Specify utf-8 for clarity.

Apply this diff:

-        return new TextDecoder().decode(uint8Array);
+        return new TextDecoder("utf-8").decode(uint8Array);

123-126: Mirror explicit encoding on write.

For symmetry with read; optional.

Apply this diff:

-        const uint8Array = typeof data === "string"
-          ? new TextEncoder().encode(data)
+        const uint8Array = typeof data === "string"
+          ? new TextEncoder().encode(data) // utf-8 by default
           : data;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea9871d and d45a8a8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (29)
  • .vscode/extensions.json (1 hunks)
  • .vscode/launch.json (1 hunks)
  • .vscode/settings.json (1 hunks)
  • .vscode/tasks.json (1 hunks)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (3 hunks)
  • vscode/.gitignore (1 hunks)
  • vscode/README.md (1 hunks)
  • vscode/eslint.config.js (1 hunks)
  • vscode/package.json (1 hunks)
  • vscode/src/commands/browse-ucd-files.ts (1 hunks)
  • vscode/src/commands/index.ts (1 hunks)
  • vscode/src/commands/open-entry.ts (1 hunks)
  • vscode/src/commands/refresh-explorer.ts (1 hunks)
  • vscode/src/commands/visualize-file.ts (1 hunks)
  • vscode/src/composables/useUCDContentProvider.ts (1 hunks)
  • vscode/src/composables/useUCDExplorer.ts (1 hunks)
  • vscode/src/composables/useUCDStore.ts (1 hunks)
  • vscode/src/config.ts (1 hunks)
  • vscode/src/extension.ts (1 hunks)
  • vscode/src/lib/files.ts (1 hunks)
  • vscode/src/lib/ucd-content-provider.ts (1 hunks)
  • vscode/src/lib/vscode-fs-bridge.ts (1 hunks)
  • vscode/src/logger.ts (1 hunks)
  • vscode/src/views/ucd-explorer.ts (1 hunks)
  • vscode/tsconfig.build.json (1 hunks)
  • vscode/tsconfig.json (1 hunks)
  • vscode/tsdown.config.ts (1 hunks)
  • vscode/turbo.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/src/**

📄 CodeRabbit inference engine (AGENTS.md)

Include all source files under any src directory in coverage

Files:

  • vscode/src/commands/index.ts
  • vscode/src/commands/browse-ucd-files.ts
  • vscode/src/extension.ts
  • vscode/src/composables/useUCDStore.ts
  • vscode/src/composables/useUCDContentProvider.ts
  • vscode/src/commands/refresh-explorer.ts
  • vscode/src/config.ts
  • vscode/src/commands/open-entry.ts
  • vscode/src/views/ucd-explorer.ts
  • vscode/src/commands/visualize-file.ts
  • vscode/src/lib/ucd-content-provider.ts
  • vscode/src/composables/useUCDExplorer.ts
  • vscode/src/logger.ts
  • vscode/src/lib/files.ts
  • vscode/src/lib/vscode-fs-bridge.ts
package.json

📄 CodeRabbit inference engine (AGENTS.md)

package.json: Set Node.js engines >= 22.17 in the root package.json
Pin TypeScript to version 5.9.2 in the root devDependencies

Files:

  • package.json
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM across the codebase (package.json "type": "module")

Files:

  • package.json
  • vscode/package.json
🧠 Learnings (5)
📚 Learning: 2025-06-29T11:20:13.668Z
Learnt from: luxass
PR: ucdjs/ucd#87
File: packages/worker-shared/tsconfig.build.json:1-4
Timestamp: 2025-06-29T11:20:13.668Z
Learning: In the ucdjs/ucd project, the packages use tsdown instead of tsc for building libraries. The tsconfig.build.json files are primarily for IDE experience and type checking, not for direct compilation, so including "test" directories in these configs doesn't affect build output.

Applied to files:

  • vscode/tsconfig.json
  • vscode/tsdown.config.ts
  • vscode/tsconfig.build.json
📚 Learning: 2025-07-20T05:37:40.565Z
Learnt from: luxass
PR: ucdjs/ucd#131
File: tooling/eslint-plugin/package.json:0-0
Timestamp: 2025-07-20T05:37:40.565Z
Learning: In the ucdjs/ucd project, internal tooling packages (private packages in the tooling/ directory) export TypeScript files directly without requiring a build step, unlike published packages which use tsdown to build and export from ./dist/. Examples include ucdjs/tsdown-config and ucdjs/eslint-plugin.

Applied to files:

  • vscode/tsconfig.json
  • vscode/tsdown.config.ts
  • vscode/package.json
  • vscode/tsconfig.build.json
📚 Learning: 2025-09-10T14:28:08.625Z
Learnt from: CR
PR: ucdjs/ucd#0
File: AGENTS.md:0-0
Timestamp: 2025-09-10T14:28:08.625Z
Learning: Applies to turbo.json : Manage build orchestration and caching via the root turbo.json

Applied to files:

  • vscode/turbo.json
  • package.json
📚 Learning: 2025-09-10T14:28:08.625Z
Learnt from: CR
PR: ucdjs/ucd#0
File: AGENTS.md:0-0
Timestamp: 2025-09-10T14:28:08.625Z
Learning: Applies to packages/*/eslint.config.js : Maintain per-package ESLint configuration in packages/*/eslint.config.js

Applied to files:

  • vscode/eslint.config.js
📚 Learning: 2025-09-10T14:28:08.625Z
Learnt from: CR
PR: ucdjs/ucd#0
File: AGENTS.md:0-0
Timestamp: 2025-09-10T14:28:08.625Z
Learning: Applies to packages/*/tsdown.config.ts : Each package should include a tsdown.config.ts for building

Applied to files:

  • vscode/tsdown.config.ts
  • vscode/tsconfig.build.json
🧬 Code graph analysis (11)
vscode/src/commands/index.ts (4)
vscode/src/commands/browse-ucd-files.ts (1)
  • useBrowseUcdFilesCommand (4-8)
vscode/src/commands/refresh-explorer.ts (1)
  • useRefreshExplorerCommand (6-19)
vscode/src/commands/visualize-file.ts (1)
  • useVisualizeFileCommand (5-9)
vscode/src/commands/open-entry.ts (1)
  • useOpenEntryCommand (9-51)
vscode/src/extension.ts (4)
vscode/src/logger.ts (1)
  • logger (3-3)
vscode/src/views/ucd-explorer.ts (1)
  • initializeUCDExplorerView (6-32)
vscode/src/commands/index.ts (1)
  • registerCommands (6-11)
vscode/src/composables/useUCDContentProvider.ts (1)
  • useUCDContentProvider (5-9)
vscode/src/composables/useUCDStore.ts (3)
vscode/src/config.ts (1)
  • config (4-7)
vscode/src/logger.ts (1)
  • logger (3-3)
vscode/src/lib/vscode-fs-bridge.ts (1)
  • vscodeFSBridge (7-144)
vscode/src/composables/useUCDContentProvider.ts (1)
vscode/src/lib/ucd-content-provider.ts (1)
  • UCDContentProvider (5-22)
vscode/src/commands/refresh-explorer.ts (2)
vscode/src/composables/useUCDExplorer.ts (1)
  • useUCDExplorer (18-93)
vscode/src/logger.ts (1)
  • logger (3-3)
vscode/src/commands/open-entry.ts (2)
vscode/src/logger.ts (1)
  • logger (3-3)
vscode/src/composables/useUCDExplorer.ts (1)
  • UCDTreeItem (11-16)
vscode/src/views/ucd-explorer.ts (2)
vscode/src/composables/useUCDExplorer.ts (2)
  • useUCDExplorer (18-93)
  • UCDTreeItem (11-16)
vscode/src/logger.ts (1)
  • logger (3-3)
vscode/src/lib/ucd-content-provider.ts (2)
vscode/src/logger.ts (1)
  • logger (3-3)
apps/web/worker/index.ts (1)
  • fetch (2-5)
vscode/src/composables/useUCDExplorer.ts (3)
vscode/src/composables/useUCDStore.ts (1)
  • useUCDStore (8-57)
vscode/src/lib/files.ts (1)
  • getFilesByVersion (46-62)
vscode/src/logger.ts (1)
  • logger (3-3)
vscode/tsdown.config.ts (1)
tooling/tsdown-config/src/factory.ts (1)
  • createTsdownConfig (25-31)
vscode/src/lib/files.ts (2)
vscode/src/composables/useUCDExplorer.ts (1)
  • UCDTreeItem (11-16)
vscode/src/logger.ts (1)
  • logger (3-3)
🔇 Additional comments (19)
.vscode/tasks.json (1)

1-26: LGTM! Well-configured background task for VS Code extension development.

The task configuration correctly sets up a background npm task with appropriate problem matchers and lifecycle patterns for TypeScript/webpack builds. The configuration aligns with VS Code extension development best practices.

package.json (1)

11-12: LGTM! Proper workspace configuration.

The addition of "vscode" to the workspaces array correctly integrates the VS Code extension into the monorepo structure.

vscode/src/logger.ts (1)

1-3: LGTM! Clean logger configuration.

The logger setup using reactive-vscode's useLogger composable with a clear namespace is appropriate for VS Code extension development. This aligns with the reactive-vscode library's best practices for structured logging.

Based on learnings

vscode/turbo.json (1)

1-27: LGTM! Well-structured Turbo configuration.

The configuration correctly:

  • Extends the root turbo configuration
  • Defines proper task dependencies (ext:gen before build/typecheck)
  • Configures appropriate outputs for generated files and build artifacts
  • Sets up non-persistent dev task for background development

The generated meta.ts pattern aligns with VS Code extension development practices.

vscode/src/commands/refresh-explorer.ts (1)

6-18: LGTM: clear and safe refresh command

Command registration and cache reset look good; logging covers both failure and success paths.

vscode/eslint.config.js (1)

1-7: ESM support confirmed vscode/package.json already sets "type": "module", so no changes required.

.vscode/settings.json (1)

16-18: Fix run-on-save matcher for Windows paths

-        "match": "vscode/package.json",
+        "match": "vscode[\\\\/]+package\\.json$",

Verify it triggers on both macOS/Linux and Windows when saving vscode/package.json.

pnpm-workspace.yaml (3)

15-15: LGTM! Workspace package addition aligns with PR objectives.

Adding the vscode workspace package is consistent with moving the VS Code extension into this repository.


85-85: LGTM! VS Code types version aligns with engine requirements.

The @types/vscode version 1.104.0 properly supports the VS Code engine requirement of ^1.100.0 specified in the extension manifest.


99-103: Verify reactive-vscode version compatibility.

The VS Code catalog specifies reactive-vscode 0.3.2, but based on learnings, the latest published version is 0.2.14 (March 2025). Please confirm this version exists and is stable.

Based on learnings about reactive-vscode versions.Based on the search results, I can see that the latest version of @reactive-vscode/vueuse is 0.2.9, published a month ago, but the workspace configuration specifies version 0.3.2. Let me search for the main reactive-vscode package specifically.The search results confirm that reactive-vscode is in 0.x and may have API changes, but don't clearly show version 0.3.2. Based on the retrieved learnings which mention the latest published version as 0.2.14 (March 2025), and the search results showing @reactive-vscode/vueuse at 0.2.9, the specified version 0.3.2 appears to be newer than what's documented. This warrants verification but may not necessarily be an error - the project could have published newer versions.

vscode/src/views/ucd-explorer.ts (2)

20-25: LGTM! Proper lazy loading implementation with version extraction.

The logic correctly:

  • Checks the contextValue to identify version folders
  • Extracts the version from the UCDTreeItem metadata
  • Calls the explorer's loadChildrenForVersion method for lazy loading

This aligns well with the useUCDExplorer composable's caching and loading logic.


26-29: LGTM! Comprehensive error handling with proper logging.

The try/catch block properly handles async errors and uses the centralized logger for consistent error reporting.

vscode/src/lib/files.ts (3)

10-13: LGTM! Proper null checking for defensive programming.

The explicit null check prevents potential runtime errors and provides a clear error message.


37-43: Well-structured metadata attachment with proper URL construction.

The __ucd metadata correctly includes version information and constructs URLs using the hasUCDFolderPath utility to handle version-specific path requirements. The recursive mapping maintains the tree structure properly.


46-62: LGTM! Proper async error handling with tuple destructuring.

The function correctly:

  • Uses tuple destructuring to handle the store's return format
  • Checks for and throws errors when present
  • Maps the data using the helper function
  • Provides comprehensive error logging and safe fallback

The error handling ensures the UI remains functional even when data loading fails.

vscode/package.json (3)

6-6: LGTM! Proper ESM configuration aligns with coding guidelines.

The "type": "module" declaration correctly enables ESM across the codebase as required by the coding guidelines.

As per coding guidelines requiring ESM configuration.


168-172: LGTM! Proper workspace dependencies for monorepo structure.

The use of workspace:* for internal packages correctly leverages the pnpm workspace setup for local development and ensures proper dependency management.

Based on learnings about internal tooling packages in the ucdjs/ucd project.


22-22: Confirmed CJS build output matches .cjs main entry
tsdown.config.ts specifies format: "cjs", so the dist/extension.cjs artifact aligns with the manifest and will be recognized as CommonJS within a "module" package.

vscode/src/commands/open-entry.ts (1)

3-3: Verify hasUCDFolderPath(version) semantics.

Confirm it expects a version string (not a filesystem path) and matches your stored structure. If it requires a path, prepend the base path appropriately.

Also applies to: 39-45

Comment thread .vscode/launch.json
Comment on lines +12 to +16
"outFiles": [
"${workspaceFolder}/vscode/dist/**/*.js"
],
"preLaunchTask": "npm: dev:vscode"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify dev task and build outputs

Ensure:

  • npm script “dev:vscode” exists (root or vscode workspace) and is wired in tasks.json
  • outFiles matches your compiled output path (dist) with source maps enabled during dev

🏁 Script executed:

#!/bin/bash
# Check npm script presence
rg -n --glob '**/package.json' -C2 '"dev:vscode"'

# Check VS Code tasks for npm: dev:vscode
rg -n -C2 '"dev:vscode"' .vscode/tasks.json

# Check tsconfig outDir for vscode package
rg -n -C2 '"outDir"' vscode/tsconfig*.json

Length of output: 119


Define missing dev:vscode npm script and configure outDir

  • package.json doesn’t define "dev:vscode" yet .vscode/tasks.json invokes it
  • no "outDir" in vscode/tsconfig*.json; set it to "vscode/dist" (with sourceMap) so it matches your launch.json outFiles

Comment thread vscode/package.json
Comment on lines +88 to +96
"ucd.data-files-api": {
"type": "string",
"description": "API URL to fetch UCD data files from",
"default": "https://unicode-api.luxass.dev",
"enum": [
"https://unicode-api.luxass.dev",
"https://preview.unicode-api.luxass.dev"
]
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Validate API URLs and consider environment configuration.

The configuration provides two hardcoded API endpoints. Consider:

  1. Verifying these URLs are accessible and return expected data
  2. Whether additional configuration for self-hosted instances might be valuable

🏁 Script executed:

#!/bin/bash
# Verify the API endpoints are accessible
echo "Testing UCD API endpoints..."

# Test production API
echo "Testing production API:"
curl -sf "https://unicode-api.luxass.dev" -I || echo "Production API not accessible"

# Test preview API  
echo "Testing preview API:"
curl -sf "https://preview.unicode-api.luxass.dev" -I || echo "Preview API not accessible"

# Check if these APIs return expected data structure
echo "Checking API response format:"
curl -sf "https://unicode-api.luxass.dev" -H "Accept: application/json" | head -c 200 || echo "No JSON response"

Length of output: 1458


Fix UCD data-files API URLs in vscode/package.json

  • Default and enum values under ucd.data-files-api point to the root URLs, which serve HTML docs (HTTP 200 text/html), not JSON data. Update them to the actual JSON endpoints (e.g. include /data-files or the correct API path).
  • Add support for custom self-hosted instances via an environment-driven override.
🤖 Prompt for AI Agents
In vscode/package.json around lines 88 to 96, the ucd.data-files-api default and
enum entries currently point to root HTML endpoints instead of the actual JSON
API path; update the default and enum values to the correct JSON endpoints (e.g.
append the proper API path such as /data-files or the real API route used by the
service) and ensure the schema allows a custom self-hosted URL by documenting
and wiring an environment-driven override (read from an env var like
UCD_DATA_FILES_API in runtime/config and prefer it over the package.json
default). Ensure the enum contains the actual JSON endpoint URLs and the default
matches a working JSON endpoint.

Comment on lines +5 to +7
useCommand(Meta.commands.browseUcdFiles, async () => {
executeCommand("ucd:explorer.focus");
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Await the focus command so failures surface properly

The async handler returns before executeCommand finishes, so any rejection turns into an unhandled promise and users receive no feedback. Await (or return) the command promise so errors propagate through the command invocation chain.

-    executeCommand("ucd:explorer.focus");
+    await executeCommand("ucd:explorer.focus");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useCommand(Meta.commands.browseUcdFiles, async () => {
executeCommand("ucd:explorer.focus");
});
useCommand(Meta.commands.browseUcdFiles, async () => {
await executeCommand("ucd:explorer.focus");
});
🤖 Prompt for AI Agents
In vscode/src/commands/browse-ucd-files.ts around lines 5 to 7, the async
command handler calls executeCommand("ucd:explorer.focus") but does not await or
return its promise so rejections become unhandled; change the handler to await
(or return) the executeCommand call so the command promise is propagated (e.g.,
await executeCommand(...)) and consider letting the caller surface the error
instead of silently dropping the promise.

Comment on lines +21 to +68
const childrenCache = ref<Map<string, TreeViewNode[]>>(new Map());
const loadingPromises = ref<Map<string, Promise<TreeViewNode[]>>>(new Map());

function refresh() {
childrenCache.value.clear();
loadingPromises.value.clear();
}

async function loadChildrenForVersion(version: string): Promise<TreeViewNode[]> {
const cached = childrenCache.value.get(version);
if (cached) {
return cached;
}

// return existing promise if already loading (prevents duplicate requests)
const existingPromise = loadingPromises.value.get(version);
if (existingPromise) {
return existingPromise;
}

// create new loading promise
const loadingPromise = (async () => {
try {
const storeValue = toRaw(store.value);
if (!(storeValue instanceof UCDStore)) {
throw new TypeError("UCD Store is not initialized");
}

const files = await getFilesByVersion(
storeValue,
version,
);

childrenCache.value.set(version, files);
return files;
} catch (error) {
logger.error(`Failed to load files for version ${version}:`, error);
return [];
} finally {
// clean up loading promise regardless of success/failure
loadingPromises.value.delete(version);
}
})();

// store the promise to prevent duplicate requests
loadingPromises.value.set(version, loadingPromise);
return loadingPromise;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix cache reactivity to avoid stale tree nodes

childrenCache (and loadingPromises) are refs that wrap a mutable Map, but we only mutate the inner map via .set(), .clear(), and .delete() without ever replacing childrenCache.value. Vue/reactive-vscode won't emit a change signal in this situation, so your computed tree never recomputes—expanded folders stay empty even after loadChildrenForVersion resolves. Reassign the ref with a fresh Map whenever you mutate it (including refresh) to keep the explorer responsive.

   const childrenCache = ref<Map<string, TreeViewNode[]>>(new Map());
   const loadingPromises = ref<Map<string, Promise<TreeViewNode[]>>>(new Map());
 
   function refresh() {
-    childrenCache.value.clear();
-    loadingPromises.value.clear();
+    childrenCache.value = new Map();
+    loadingPromises.value = new Map();
   }
 
   async function loadChildrenForVersion(version: string): Promise<TreeViewNode[]> {
@@
-        const files = await getFilesByVersion(
-          storeValue,
-          version,
-        );
-
-        childrenCache.value.set(version, files);
+        const files = await getFilesByVersion(storeValue, version);
+        const nextCache = new Map(childrenCache.value);
+        nextCache.set(version, files);
+        childrenCache.value = nextCache;
         return files;
       } catch (error) {
         logger.error(`Failed to load files for version ${version}:`, error);
         return [];
       } finally {
         // clean up loading promise regardless of success/failure
-        loadingPromises.value.delete(version);
+        const nextPromises = new Map(loadingPromises.value);
+        nextPromises.delete(version);
+        loadingPromises.value = nextPromises;
       }
     })();
 
     // store the promise to prevent duplicate requests
-    loadingPromises.value.set(version, loadingPromise);
+    const nextPromises = new Map(loadingPromises.value);
+    nextPromises.set(version, loadingPromise);
+    loadingPromises.value = nextPromises;
     return loadingPromise;
   }

Based on learnings

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const childrenCache = ref<Map<string, TreeViewNode[]>>(new Map());
const loadingPromises = ref<Map<string, Promise<TreeViewNode[]>>>(new Map());
function refresh() {
childrenCache.value.clear();
loadingPromises.value.clear();
}
async function loadChildrenForVersion(version: string): Promise<TreeViewNode[]> {
const cached = childrenCache.value.get(version);
if (cached) {
return cached;
}
// return existing promise if already loading (prevents duplicate requests)
const existingPromise = loadingPromises.value.get(version);
if (existingPromise) {
return existingPromise;
}
// create new loading promise
const loadingPromise = (async () => {
try {
const storeValue = toRaw(store.value);
if (!(storeValue instanceof UCDStore)) {
throw new TypeError("UCD Store is not initialized");
}
const files = await getFilesByVersion(
storeValue,
version,
);
childrenCache.value.set(version, files);
return files;
} catch (error) {
logger.error(`Failed to load files for version ${version}:`, error);
return [];
} finally {
// clean up loading promise regardless of success/failure
loadingPromises.value.delete(version);
}
})();
// store the promise to prevent duplicate requests
loadingPromises.value.set(version, loadingPromise);
return loadingPromise;
}
const childrenCache = ref<Map<string, TreeViewNode[]>>(new Map());
const loadingPromises = ref<Map<string, Promise<TreeViewNode[]>>>(new Map());
function refresh() {
childrenCache.value = new Map();
loadingPromises.value = new Map();
}
async function loadChildrenForVersion(version: string): Promise<TreeViewNode[]> {
const cached = childrenCache.value.get(version);
if (cached) {
return cached;
}
// return existing promise if already loading (prevents duplicate requests)
const existingPromise = loadingPromises.value.get(version);
if (existingPromise) {
return existingPromise;
}
// create new loading promise
const loadingPromise = (async () => {
try {
const storeValue = toRaw(store.value);
if (!(storeValue instanceof UCDStore)) {
throw new TypeError("UCD Store is not initialized");
}
const files = await getFilesByVersion(storeValue, version);
const nextCache = new Map(childrenCache.value);
nextCache.set(version, files);
childrenCache.value = nextCache;
return files;
} catch (error) {
logger.error(`Failed to load files for version ${version}:`, error);
return [];
} finally {
// clean up loading promise regardless of success/failure
const nextPromises = new Map(loadingPromises.value);
nextPromises.delete(version);
loadingPromises.value = nextPromises;
}
})();
// store the promise to prevent duplicate requests
const nextPromises = new Map(loadingPromises.value);
nextPromises.set(version, loadingPromise);
loadingPromises.value = nextPromises;
return loadingPromise;
}
🤖 Prompt for AI Agents
In vscode/src/composables/useUCDExplorer.ts around lines 21 to 68, childrenCache
and loadingPromises are refs wrapping Map objects but are only mutated in-place
(.set, .delete, .clear), so Vue won't react; update the code to reassign the ref
to a new Map whenever you mutate it (e.g., after setting files do
childrenCache.value = new Map(childrenCache.value), after deleting a key do
loadingPromises.value = new Map(loadingPromises.value), and in refresh replace
both refs with new Map() instances) to ensure the computed tree recomputes and
expanded folders update correctly.

Comment on lines +30 to +36
try {
await _store.init();
} catch (error) {
console.error("Failed to initialize UCD store:", error);
}

return _store;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid returning a half-initialized store; propagate init failure.

If init fails, rethrow so callers can handle (we set store to null upstream).

Apply this diff:

     try {
       await _store.init();
     } catch (error) {
-      console.error("Failed to initialize UCD store:", error);
+      logger.error("Failed to initialize UCD store:", error as Error);
+      throw error;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
await _store.init();
} catch (error) {
console.error("Failed to initialize UCD store:", error);
}
return _store;
try {
await _store.init();
} catch (error) {
logger.error("Failed to initialize UCD store:", error as Error);
throw error;
}
return _store;
🤖 Prompt for AI Agents
In vscode/src/composables/useUCDStore.ts around lines 30 to 36, the current code
catches init errors and logs them then returns the store, which may be
half-initialized; change this to propagate the failure: remove the swallowing
catch (or rethrow after logging) so that when _store.init() rejects the error is
thrown to callers (upstream expects null on failure) — ensure the function does
not return _store on init failure.

Comment on lines +39 to +54
watch(
() => config["local-store-path"],
async (newVal, oldVal) => {
if (newVal === oldVal) {
return;
}

try {
store.value = await createStoreFromConfig(newVal);
} catch (error) {
console.error("Failed to create UCD store:", error);
store.value = null;
}
},
{ immediate: true },
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against race conditions on fast config changes (last-write-wins).

Out-of-order async resolutions can overwrite with stale stores. Use a sequence token to ensure only the latest result wins; optionally dispose the previous store if supported.

Apply this diff inside the watcher:

-    async (newVal, oldVal) => {
+    async (newVal, oldVal) => {
       if (newVal === oldVal) {
         return;
       }
 
-      try {
-        store.value = await createStoreFromConfig(newVal);
+      try {
+        const seq = ++createSeq;
+        const next = await createStoreFromConfig(newVal);
+        if (seq !== createSeq) return; // stale
+        const prev = store.value;
+        store.value = next;
+        // optional cleanup if supported by the implementation
+        if (prev && typeof (prev as any).dispose === "function") {
+          try { (prev as any).dispose(); } catch {}
+        }
       } catch (error) {
-        console.error("Failed to create UCD store:", error);
+        logger.error("Failed to create UCD store:", error as Error);
         store.value = null;
       }
     },

And add this variable near the top of the composable (outside the watcher):

// add after `const store = ref<UCDStore | null>(null);`
let createSeq = 0;
🤖 Prompt for AI Agents
In vscode/src/composables/useUCDStore.ts around lines 39 to 54, the watcher can
suffer race conditions where slower async createStoreFromConfig calls overwrite
newer results; add a numeric sequence token to enforce last-write-wins: declare
let createSeq = 0 just after const store = ref<UCDStore | null>(null); then
inside the watcher increment a local token (e.g., const seq = ++createSeq)
before awaiting createStoreFromConfig and after the await check that seq ===
createSeq before assigning store.value; if a previous store instance exists and
the new assignment is winning, dispose/close the old store if the store API
supports it to avoid leaks.

Comment thread vscode/tsdown.config.ts
Comment on lines +9 to +11
external: [
"vscode",
],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Update external dependency configuration.

The external property should be replaced with externalIncludes to match the modern tsdown API. Based on the retrieved learnings about reactive-vscode, this configuration should align with current best practices.

-  external: [
-    "vscode",
-  ],
+  externalIncludes: ["vscode"],

Based on learnings

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
external: [
"vscode",
],
externalIncludes: ["vscode"],
🤖 Prompt for AI Agents
In vscode/tsdown.config.ts around lines 9 to 11, the config uses the outdated
property name "external"; replace that key with "externalIncludes" and keep the
same array contents (e.g., ["vscode"]) so the config matches the modern tsdown
API and current reactive-vscode best practices.

Comment thread vscode/tsdown.config.ts
Comment on lines +12 to +15
// Bundle workspace packages so they're included in the extension
noExternal: [
/^@ucdjs\//,
],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Update bundling configuration property.

The noExternal property should be replaced with noExternalIncludes to match the modern tsdown API.

-  // Bundle workspace packages so they're included in the extension
-  noExternal: [
-    /^@ucdjs\//,
-  ],
+  // Bundle workspace packages so they're included in the extension
+  noExternalIncludes: [/^@ucdjs\//],

Based on learnings

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Bundle workspace packages so they're included in the extension
noExternal: [
/^@ucdjs\//,
],
// Bundle workspace packages so they're included in the extension
noExternalIncludes: [/^@ucdjs\//],
🤖 Prompt for AI Agents
In vscode/tsdown.config.ts around lines 12 to 15, the config uses the legacy
noExternal property; replace it with noExternalIncludes to match the current
tsdown API, keeping the same array contents (the /^@ucdjs\// regex) and ensure
the property name is updated only (no other structural changes).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant