feat: add Korean-English localization and light theme
Localize studio, curriculum, visualization and loading interfaces. Add persistent language and theme settings, refine module loading states, and normalize remote shell locales.
This commit is contained in:
+19
-12
@@ -3,6 +3,7 @@ import { checkDockerStatus } from './dockerStatus'
|
||||
import { cameraRuntimeArtifacts } from './cameraRuntime'
|
||||
import { remoteModuleFilesScript } from './remoteModuleFiles'
|
||||
import { resolveContainerTerminal } from './containerTerminal'
|
||||
import { remoteShellCommand, remoteShellEnvironment } from './remoteShell'
|
||||
import { yahboomDevicePreflight, yahboomModuleCases, yahboomRuntimeImage, yahboomRuntimeArtifacts, yahboomSimulation } from './yahboomRuntime'
|
||||
import { visualizationProgress, reportVisualizationProgress } from './visualizationProgress'
|
||||
import { moduleProgress, reportModuleProgress, createModuleProgressParser } from './moduleProgress'
|
||||
@@ -1723,10 +1724,11 @@ function handleModuleOperation(channel: string, handler: Parameters<typeof ipcMa
|
||||
})
|
||||
}
|
||||
|
||||
async function runCommandOutput(command: string, args: string[], cwd?: string, env?: NodeJS.ProcessEnv, timeoutMs = 120_000): Promise<string> {
|
||||
async function runCommandOutput(command: string, args: string[], cwd?: string, env?: NodeJS.ProcessEnv, timeoutMs = 120_000, outputMode: 'combined' | 'stdout' = 'combined'): Promise<string> {
|
||||
const readProgress = createModuleProgressParser()
|
||||
return new Promise<string>((resolvePromise, rejectPromise) => {
|
||||
let output = ''
|
||||
let stdout = ''
|
||||
let terminated = false
|
||||
const child = spawn(command, args, {
|
||||
cwd,
|
||||
@@ -1756,6 +1758,7 @@ async function runCommandOutput(command: string, args: string[], cwd?: string, e
|
||||
|
||||
child.stdout.setEncoding('utf8')
|
||||
child.stdout.on('data', (data: string) => {
|
||||
stdout += data.toString()
|
||||
output += data.toString()
|
||||
readProgress(data.toString())
|
||||
})
|
||||
@@ -1769,7 +1772,7 @@ async function runCommandOutput(command: string, args: string[], cwd?: string, e
|
||||
child.on('close', (code) => {
|
||||
clearTimeout(timeout)
|
||||
if (terminated) return
|
||||
if (code === 0) resolvePromise(output)
|
||||
if (code === 0) resolvePromise(outputMode === 'stdout' ? stdout : output)
|
||||
else rejectPromise(new Error(output.trim() || `${command} ${args.join(' ')} failed with exit code ${code ?? 'unknown'}`))
|
||||
})
|
||||
})
|
||||
@@ -2135,11 +2138,12 @@ async function runSshOutput(target: RemoteWorkspaceTarget, command: string, time
|
||||
'-i',
|
||||
normalized.identityFile,
|
||||
`${normalized.user}@${normalized.host}`,
|
||||
command
|
||||
remoteShellCommand(command)
|
||||
],
|
||||
undefined,
|
||||
undefined,
|
||||
timeoutMs
|
||||
remoteShellEnvironment(process.env),
|
||||
timeoutMs,
|
||||
'stdout'
|
||||
))
|
||||
}
|
||||
|
||||
@@ -2167,11 +2171,12 @@ async function runUnqueuedSshOutput(target: RemoteWorkspaceTarget, command: stri
|
||||
'-i',
|
||||
normalized.identityFile,
|
||||
`${normalized.user}@${normalized.host}`,
|
||||
command,
|
||||
remoteShellCommand(command),
|
||||
],
|
||||
undefined,
|
||||
undefined,
|
||||
remoteShellEnvironment(process.env),
|
||||
timeoutMs,
|
||||
'stdout',
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5704,7 +5709,7 @@ async function readRemoteWorkspaceModuleLogs(
|
||||
if (!existsOutput.trim()) return { ok: true, logs: '', message: '생성된 컨테이너가 없습니다.' }
|
||||
|
||||
try {
|
||||
const logs = await runSshOutput(target, `docker logs --timestamps --tail 300 ${quoteShellSingle(containerKey)}`, 10000)
|
||||
const logs = await runSshOutput(target, `docker logs --timestamps --tail 300 ${quoteShellSingle(containerKey)} 2>&1`, 10000)
|
||||
return { ok: true, logs }
|
||||
} catch (error) {
|
||||
return { ok: false, logs: '', message: error instanceof Error ? error.message : String(error) }
|
||||
@@ -5980,7 +5985,8 @@ async function containerTerminalCommand(moduleRoot: string, moduleName: string |
|
||||
const container = resolveContainerTerminal(output, moduleRoot)
|
||||
const shell = buildWorkspaceShellCommand(moduleName, container.cwd, cols, rows, false)
|
||||
const init = 'if [ -n "$ROS_DISTRO" ] && [ -f "/opt/ros/$ROS_DISTRO/setup.bash" ]; then source "/opt/ros/$ROS_DISTRO/setup.bash"; fi; if [ -f ./install/setup.bash ]; then source ./install/setup.bash; fi; exec ' + shell
|
||||
return `exec docker exec -it -e TERM=xterm-256color -w ${quoteShellSingle(container.cwd)} ${quoteShellSingle(container.id)} /bin/bash --noprofile --norc -c ${quoteShellSingle(init)}`
|
||||
const containerShell = target ? remoteShellCommand(init) : `/bin/bash --noprofile --norc -c ${quoteShellSingle(init)}`
|
||||
return `exec docker exec -it -e TERM=xterm-256color -w ${quoteShellSingle(container.cwd)} ${quoteShellSingle(container.id)} ${containerShell}`
|
||||
}
|
||||
|
||||
function registerTerminalHandlers(): void {
|
||||
@@ -6079,14 +6085,15 @@ function registerTerminalHandlers(): void {
|
||||
'-i',
|
||||
target.identityFile,
|
||||
`${target.user}@${target.host}`,
|
||||
mode === 'container'
|
||||
remoteShellCommand(mode === 'container'
|
||||
? await containerTerminalCommand(resolved.moduleRoot, moduleName, safeCols, safeRows, target)
|
||||
: buildWorkspaceShellCommand(moduleName, resolved.moduleRoot, safeCols, safeRows, true)
|
||||
: buildWorkspaceShellCommand(moduleName, resolved.moduleRoot, safeCols, safeRows, true))
|
||||
], {
|
||||
name: 'xterm-256color',
|
||||
cols: safeCols,
|
||||
rows: safeRows,
|
||||
env: terminalEnvironment(safeCols, safeRows)
|
||||
env: Object.fromEntries(Object.entries(remoteShellEnvironment(terminalEnvironment(safeCols, safeRows)))
|
||||
.filter((entry): entry is [string, string] => typeof entry[1] === 'string'))
|
||||
})
|
||||
|
||||
attachTerminalProcess(event.sender, sessionId, terminalKey, proc)
|
||||
|
||||
Reference in New Issue
Block a user