Skip to content

Commit 89774b1

Browse files
committed
feat(studio): add nodepod terminal, AI chat, preview, git, extensions, and fs sync
- Add Nodepod as git submodule (third-party/nodepod) with workspace:* link - Integrate Nodepod terminal backend with xterm.js bridged to Monaco terminal - Add bidirectional filesystem sync between Nodepod and VS Code explorer - Add AI chat panel with mock xyd AI participant via chat service override - Add server preview panel that auto-opens when Nodepod detects a running server - Add git commands (init, clone, commit, etc.) via custom extension - Add rollup-vsix-plugin for loading Symbols icon theme from .vsix - Add extension gallery (OpenVSX) for installing GitHub Light Default theme - Add SCM, preferences, title bar, banner, and status bar service overrides - Switch to full workbench service override for native VS Code layout and resize
1 parent eb53aca commit 89774b1

16 files changed

Lines changed: 2865 additions & 383 deletions

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "third-party/nodepod"]
2+
path = third-party/nodepod
3+
url = https://github.com/livesession/Nodepod

apps/studio/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@codingame/monaco-vscode-storage-service-override": "~30.0.1",
3131
"@codingame/monaco-vscode-textmate-service-override": "~30.0.1",
3232
"@codingame/monaco-vscode-theme-defaults-default-extension": "~30.0.1",
33+
"@codingame/monaco-vscode-theme-seti-default-extension": "~30.0.1",
3334
"@codingame/monaco-vscode-theme-service-override": "~30.0.1",
3435
"@codingame/monaco-vscode-views-service-override": "~30.0.1",
3536
"@codingame/monaco-vscode-workbench-service-override": "~30.0.1",
@@ -39,6 +40,9 @@
3940
"@codingame/monaco-vscode-view-title-bar-service-override": "~30.0.1",
4041
"@codingame/monaco-vscode-view-banner-service-override": "~30.0.1",
4142
"@codingame/monaco-vscode-output-service-override": "~30.0.1",
43+
"@codingame/monaco-vscode-preferences-service-override": "~30.0.1",
44+
"@codingame/monaco-vscode-scm-service-override": "~30.0.1",
45+
"@codingame/monaco-vscode-git-base-default-extension": "~30.0.1",
4246
"@codingame/monaco-vscode-markers-service-override": "~30.0.1",
4347
"@codingame/monaco-vscode-log-service-override": "~30.0.1",
4448
"@codingame/monaco-vscode-dialogs-service-override": "~30.0.1",
@@ -56,13 +60,18 @@
5660
"@codingame/monaco-vscode-standalone-css-language-features": "~30.0.1",
5761
"@codingame/monaco-vscode-standalone-html-language-features": "~30.0.1",
5862
"monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~30.0.1",
63+
"@scelar/nodepod": "workspace:*",
64+
"@xterm/xterm": "^6.0.0",
65+
"@xterm/addon-fit": "^0.11.0",
66+
"@xterm/addon-webgl": "^0.19.0",
5967
"vscode": "npm:@codingame/monaco-vscode-extension-api@~30.0.1",
6068
"react": "^19.1.0",
6169
"react-dom": "^19.1.0"
6270
},
6371
"devDependencies": {
6472
"@types/react": "^19.1.2",
6573
"@types/react-dom": "^19.1.2",
74+
"@codingame/monaco-vscode-rollup-vsix-plugin": "~30.0.1",
6675
"@vitejs/plugin-react": "^6.0.1",
6776
"typescript": "^5.6.2",
6877
"vite": "^8.0.9"

apps/studio/src/App.tsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
11
import { useEffect, useRef, useState } from 'react'
22
import { ensureServicesInitialized } from './setup'
33
import { registerAiChat } from './features/ai'
4+
import { registerPreview } from './features/preview'
5+
import { registerGitCommands } from './features/git'
46

5-
// Theme defaults
6-
import '@codingame/monaco-vscode-theme-defaults-default-extension'
7+
// Bundled + VSIX extensions (static imports)
8+
import './extensions'
79

8-
// Language extensions
9-
import '@codingame/monaco-vscode-javascript-default-extension'
10-
import '@codingame/monaco-vscode-typescript-basics-default-extension'
11-
import '@codingame/monaco-vscode-json-default-extension'
12-
import '@codingame/monaco-vscode-css-default-extension'
13-
import '@codingame/monaco-vscode-html-default-extension'
14-
import '@codingame/monaco-vscode-markdown-basics-default-extension'
15-
import '@codingame/monaco-vscode-yaml-default-extension'
16-
17-
// Standalone language features
18-
import '@codingame/monaco-vscode-standalone-typescript-language-features'
19-
import '@codingame/monaco-vscode-standalone-json-language-features'
20-
import '@codingame/monaco-vscode-standalone-css-language-features'
21-
import '@codingame/monaco-vscode-standalone-html-language-features'
10+
import {
11+
waitForVsixExtensions,
12+
installMarketplaceExtensions,
13+
applyDefaultExtensionSettings,
14+
} from './extensions'
2215

2316
export function App() {
2417
const [error, setError] = useState<string | null>(null)
@@ -33,7 +26,19 @@ export function App() {
3326
try {
3427
await ensureServicesInitialized(container)
3528
if (disposed) return
29+
30+
// Wait for VSIX extensions (Symbols icons)
31+
await waitForVsixExtensions()
32+
3633
await registerAiChat()
34+
await registerPreview()
35+
await registerGitCommands()
36+
37+
// Install marketplace extensions (GitHub Theme) + apply settings
38+
const vscodeApi = await import('vscode')
39+
await installMarketplaceExtensions(vscodeApi)
40+
await applyDefaultExtensionSettings(vscodeApi)
41+
3742
console.log('[xyd studio] initialized')
3843
} catch (e: any) {
3944
console.error('[xyd studio] init failed:', e)

apps/studio/src/extensions.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* Default extensions for xyd studio.
3+
*
4+
* 1. Bundled extensions (npm @codingame packages) — static imports
5+
* 2. VSIX extensions — loaded via rollup-vsix-plugin at build time
6+
* 3. Marketplace extensions — installed from OpenVSX at runtime
7+
*/
8+
9+
// --- Bundled extensions (static imports) ---
10+
11+
// Theme defaults (Dark Modern, Light Modern, Dark+, Light+, etc.)
12+
import '@codingame/monaco-vscode-theme-defaults-default-extension'
13+
14+
// Seti file icons (fallback)
15+
import '@codingame/monaco-vscode-theme-seti-default-extension'
16+
17+
// Language grammars
18+
import '@codingame/monaco-vscode-javascript-default-extension'
19+
import '@codingame/monaco-vscode-typescript-basics-default-extension'
20+
import '@codingame/monaco-vscode-json-default-extension'
21+
import '@codingame/monaco-vscode-css-default-extension'
22+
import '@codingame/monaco-vscode-html-default-extension'
23+
import '@codingame/monaco-vscode-markdown-basics-default-extension'
24+
import '@codingame/monaco-vscode-yaml-default-extension'
25+
26+
// Language features (intellisense)
27+
import '@codingame/monaco-vscode-standalone-typescript-language-features'
28+
import '@codingame/monaco-vscode-standalone-json-language-features'
29+
import '@codingame/monaco-vscode-standalone-css-language-features'
30+
import '@codingame/monaco-vscode-standalone-html-language-features'
31+
32+
// Git support (provides Git: Clone, Git: Init, etc. commands)
33+
import '@codingame/monaco-vscode-git-base-default-extension'
34+
35+
// --- VSIX extensions (loaded at build time via rollup-vsix-plugin) ---
36+
37+
import { whenReady as symbolsReady } from '../symbols-web-0.0.25.vsix'
38+
39+
export async function waitForVsixExtensions() {
40+
await symbolsReady
41+
console.log('[xyd studio] VSIX extension loaded: Symbols icon theme')
42+
}
43+
44+
// --- Marketplace extensions ---
45+
46+
interface MarketplaceExtension {
47+
id: string
48+
displayName: string
49+
}
50+
51+
const marketplaceExtensions: MarketplaceExtension[] = [
52+
{ id: 'GitHub.github-vscode-theme', displayName: 'GitHub Theme' },
53+
]
54+
55+
export async function installMarketplaceExtensions(
56+
vscodeApi: typeof import('vscode')
57+
) {
58+
for (const ext of marketplaceExtensions) {
59+
try {
60+
await vscodeApi.commands.executeCommand(
61+
'workbench.extensions.installExtension',
62+
ext.id
63+
)
64+
console.log(`[xyd studio] Installed extension: ${ext.displayName}`)
65+
} catch (e) {
66+
console.warn(`[xyd studio] Failed to install ${ext.displayName}:`, e)
67+
}
68+
}
69+
}
70+
71+
// --- Apply defaults ---
72+
73+
export async function applyDefaultExtensionSettings(
74+
vscodeApi: typeof import('vscode')
75+
) {
76+
try {
77+
const config = vscodeApi.workspace.getConfiguration()
78+
await config.update(
79+
'workbench.colorTheme',
80+
'GitHub Light Default',
81+
vscodeApi.ConfigurationTarget.Global
82+
)
83+
await config.update(
84+
'workbench.iconTheme',
85+
'symbols',
86+
vscodeApi.ConfigurationTarget.Global
87+
)
88+
console.log('[xyd studio] Applied: GitHub Light Default + Symbols icons')
89+
} catch (e) {
90+
console.warn('[xyd studio] Failed to apply default settings:', e)
91+
}
92+
}

apps/studio/src/features/git.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { registerExtension } from '@codingame/monaco-vscode-api/extensions'
2+
import { ExtensionHostKind } from '@codingame/monaco-vscode-extensions-service-override'
3+
4+
/**
5+
* Registers Git-related commands in the command palette.
6+
* In the browser, these use Nodepod's virtual git (shell builtin)
7+
* via the terminal, since there's no native git binary.
8+
*/
9+
export async function registerGitCommands() {
10+
const { getApi } = registerExtension(
11+
{
12+
name: 'xydStudioGit',
13+
publisher: 'xyd',
14+
version: '1.0.0',
15+
engines: { vscode: '*' },
16+
contributes: {
17+
commands: [
18+
{ command: 'xyd-git.init', title: 'Git: Initialize Repository' },
19+
{ command: 'xyd-git.clone', title: 'Git: Clone' },
20+
{ command: 'xyd-git.status', title: 'Git: Status' },
21+
{ command: 'xyd-git.add', title: 'Git: Stage All Changes' },
22+
{ command: 'xyd-git.commit', title: 'Git: Commit' },
23+
{ command: 'xyd-git.log', title: 'Git: Log' },
24+
{ command: 'xyd-git.diff', title: 'Git: Diff' },
25+
{ command: 'xyd-git.branch', title: 'Git: Create Branch' },
26+
{ command: 'xyd-git.checkout', title: 'Git: Checkout to...' },
27+
],
28+
},
29+
},
30+
ExtensionHostKind.LocalProcess,
31+
{ system: true }
32+
)
33+
34+
const vscodeApi = await getApi()
35+
36+
// Helper: run a command in the terminal
37+
async function runInTerminal(command: string) {
38+
const terminal =
39+
vscodeApi.window.activeTerminal ??
40+
vscodeApi.window.createTerminal('Git')
41+
terminal.show()
42+
terminal.sendText(command)
43+
}
44+
45+
vscodeApi.commands.registerCommand('xyd-git.init', async () => {
46+
await runInTerminal('git init')
47+
})
48+
49+
vscodeApi.commands.registerCommand('xyd-git.clone', async () => {
50+
const url = await vscodeApi.window.showInputBox({
51+
prompt: 'Repository URL',
52+
placeHolder: 'https://github.com/user/repo.git',
53+
})
54+
if (url) {
55+
await runInTerminal(`git clone ${url}`)
56+
}
57+
})
58+
59+
vscodeApi.commands.registerCommand('xyd-git.status', async () => {
60+
await runInTerminal('git status')
61+
})
62+
63+
vscodeApi.commands.registerCommand('xyd-git.add', async () => {
64+
await runInTerminal('git add -A')
65+
})
66+
67+
vscodeApi.commands.registerCommand('xyd-git.commit', async () => {
68+
const message = await vscodeApi.window.showInputBox({
69+
prompt: 'Commit message',
70+
placeHolder: 'feat: my changes',
71+
})
72+
if (message) {
73+
await runInTerminal(`git commit -m "${message}"`)
74+
}
75+
})
76+
77+
vscodeApi.commands.registerCommand('xyd-git.log', async () => {
78+
await runInTerminal('git log --oneline -20')
79+
})
80+
81+
vscodeApi.commands.registerCommand('xyd-git.diff', async () => {
82+
await runInTerminal('git diff')
83+
})
84+
85+
vscodeApi.commands.registerCommand('xyd-git.branch', async () => {
86+
const name = await vscodeApi.window.showInputBox({
87+
prompt: 'Branch name',
88+
placeHolder: 'feat/my-feature',
89+
})
90+
if (name) {
91+
await runInTerminal(`git checkout -b ${name}`)
92+
}
93+
})
94+
95+
vscodeApi.commands.registerCommand('xyd-git.checkout', async () => {
96+
const branch = await vscodeApi.window.showInputBox({
97+
prompt: 'Branch to checkout',
98+
placeHolder: 'main',
99+
})
100+
if (branch) {
101+
await runInTerminal(`git checkout ${branch}`)
102+
}
103+
})
104+
105+
console.log('[xyd studio] Git commands registered')
106+
}

0 commit comments

Comments
 (0)