Skip to content

T-MVW5-code-editor-package

State: planning/proposed · Impact: medium · Complexity: medium

A skill carries scripts, JSON and YAML beside its note, and determined has nowhere to show them: the shell’s Browse page prints file text into a <pre>, and Agent Manager has no code surface at all. Three ad-hoc CodeMirror 6 mounts already exist across the repo, each with its own theme and its own language wiring. One package gives every determined surface a themed, read-or-write code editor, and gives the preview and related-files panes something to render into.

LocationRole today
solutions/determined/packages/shell/app/pages/apps/browse.vueRenders a file’s text into <pre class="browse-body">: no highlighting, no editing, no language awareness
solutions/determined/experiences/polish/app/components/PolishCodePreviewPane.vueAd-hoc CM6 mount: markdown only, read-only, themed with oneDark rather than the --d-* tokens
apps/agentpants/src-nuxt/app/components/preview/CodeMirrorViewer.vueThe standalone viewer’s own CM6 mount, with a line-highlight state field and oneDark + defaultHighlightStyle
apps/agentpants/src-nuxt/app/components/preview/RendererRegistry.tsThat mount’s language registry: markdown, yaml, json and the rest, each lazily imported
solutions/augmented/apps/vimit_prototype/app/components/VimEditor/VimEditor.vue@replit/codemirror-vim over basicSetup, with the extension-ordering and keymap-precedence prior art
packages/ts/obsidian-editor/src/theme.ts#obsidianEditorThemeThe theming contract to copy: every color is var(--obsmd-*, fallback) and a host themes by setting variables on an ancestor, never by overriding selectors
packages/ts/ui/app/assets/css/tokens.cssThe --d-* token set a determined surface themes from
solutions/determined/packages/app-state/The tier-0 package shape: package.json, moon.yml, tsconfig.json, src/
packages/ts/markdown-ui/nuxt.config.tsThe Nuxt-layer shape a component-bearing package uses so consumers get auto-imports
.moon/workspace.ymlsolutions/determined/packages/* is not glob-discovered; each project is registered by id
docs/LIBRARIES.mdThe tier-0 table, one row per solution-local package
docs/planning/decisions/D-0001-project-structure.mdThe placement gradient: start solution-local, promote on a consumer outside the origin solution

A new tier-0 package solutions/determined/packages/code-editor, published in-repo as @determined/code-editor. Both named consumers are determined, so D-0001 puts it in that solution and a second-solution consumer is what would promote it later. The package name does not change on promotion.

It ships one Vue component, CodeSurface.vue, over CodeMirror 6:

  • languages from @codemirror/lang-json, -yaml, -javascript and -python, plus a @codemirror/legacy-modes fallback for shell and anything else, resolved from a file extension and imported lazily;
  • read or write, with readOnly and an onSave emit;
  • themed entirely through --d-* tokens with neutral fallbacks, on the obsidian-editor contract: no hardcoded palette and no oneDark;
  • an extension slot a host fills, which is where the vim extension goes.

browse.vue mounts it instead of the <pre>. Agent Manager’s preview and related-files panes mount it for file contents. docs/LIBRARIES.md gains a tier-0 row. PolishCodePreviewPane.vue is named as the migration candidate and is not migrated here.

  1. Scaffold the package on the shape app-state and markdown-ui establish: package.json, moon.yml, tsconfig.json, and register the project id in .moon/workspace.yml. Decision inside this step: ship it as a Nuxt layer, matching markdown-ui, so a consuming layer gets the component by auto-import rather than a manual registration in each product.
  2. Write the language resolver — extension to lazily-imported LanguageSupport, with a plain-text fallback. RendererRegistry.ts in the standalone viewer is the working reference for the lazy shape.
  3. Write CodeSurface.vue: mount, readOnly, onSave, document-swap on a content change, teardown on unmount.
  4. Write the theme extension over --d-* with fallbacks, following obsidianEditorTheme’s rule that a host sets variables and never overrides selectors.
  5. Add the extension slot the vim extension will occupy. Decision inside this step: this task ships the slot only; T-FG8R-editor-settings-toolbar-and-vim owns the vim dependency and the setting that turns it on.
  6. Swap browse.vue’s <pre> for CodeSurface in read-only mode, keeping the existing data-browse-content hook so the shell’s tests still address it.
  7. Declare the dependency in the agent-pants experience so the preview and related panes can mount it, and add the tier-0 row to docs/LIBRARIES.md naming PolishCodePreviewPane.vue as the migration candidate.
AreaNote
solutions/determined/packages/code-editor/package.json@determined/code-editor, CM6 and language dependencies
solutions/determined/packages/code-editor/moon.ymlProject config on the app-state pattern
solutions/determined/packages/code-editor/tsconfig.jsonPackage tsconfig
solutions/determined/packages/code-editor/nuxt.config.tsLayer entry point
solutions/determined/packages/code-editor/app/components/CodeSurface.vueThe editor component
solutions/determined/packages/code-editor/app/utils/languages.tsExtension to lazy LanguageSupport, with the plain-text fallback
solutions/determined/packages/code-editor/app/utils/theme.tsThe --d-* theme extension
solutions/determined/packages/code-editor/test/languages.test.tsExtension resolution, including the fallback
.moon/workspace.ymlRegister the code-editor project id
solutions/determined/packages/shell/package.jsonDepend on @determined/code-editor
solutions/determined/packages/shell/nuxt.config.tsExtend the layer
solutions/determined/packages/shell/app/pages/apps/browse.vueMount CodeSurface read-only in place of the <pre>
solutions/determined/experiences/agent-pants/package.jsonDepend on the package for the preview and related panes
solutions/determined/experiences/agent-pants/nuxt.config.tsExtend the layer
docs/LIBRARIES.mdTier-0 row, naming the migration candidate
bun.lockRoot lockfile regeneration
  • AC-1: solutions/determined/packages/code-editor exists, is registered in .moon/workspace.yml, and its check task passes.
  • AC-2: browse.vue renders CodeSurface and no longer contains a <pre> for file content.
  • AC-3: Opening a .json, a .yaml, a .py, a .js and a .sh file in Browse highlights each one; an unknown extension falls back to plain text without an error.
  • AC-4: CodeSurface in write mode emits save on the save keybinding, and readOnly blocks every edit.
  • AC-5: The package contains no oneDark import and no literal hex color — every color is a var(--d-*, fallback).
  • AC-6: The editor is legible in both the light and the dark appearance, with no rule outside the --d-* contract.
  • AC-7: docs/LIBRARIES.md carries the tier-0 row and names PolishCodePreviewPane.vue as the migration candidate.
  • AC-8: bun install --frozen-lockfile passes at the root.
  • Migrating PolishCodePreviewPane.vue or the standalone viewer’s CodeMirrorViewer.vue. Both are named as candidates; neither moves here.
  • Vim mode and the editor settings section: T-FG8R-editor-settings-toolbar-and-vim.
  • Diffs. DiffViewer.vue in the standalone viewer stays where it is; the git panel (T-L7YP-git-panel-commit-with-validation) decides what it needs.
  • Markdown. Markdown bodies stay on MdSurface from markdown-ui; this surface is for the files a note carries.
  • Promotion to tier 1. It stays solution-local until a consumer outside determined imports it.
  • none

Member of milestone M-BMFD, from the user’s planning request of 2026-09-07 on the editor workbench: the files a skill carries need a viewer, and the preview and related-files panes need somewhere to render source. Placement follows the D-0001-project-structure gradient, and the theming rule follows packages/ts/obsidian-editor/src/theme.ts. The three existing CM6 mounts named in Today are the prior art this package consolidates.


← Back to Tasks