Skip to content

Keep Prism's terminal and Prism Terminal in sync: one shared core #15

Closed

Description

@Maxaubert

Owner, 2026-09-19: "this terminal app we're building is just an extraction of the main Prism app and should therefore be reflected in both apps. How can we best make sure the two terminals are synced and serve the same options and features?"

Planned, not started. Needs an owner decision on approach and order (below).

The problem, stated plainly

Prism Terminal was made by COPYING Prism's terminal (Prism 4196c3a, 2026-09-18), on the recommendation "new repo, Prism untouched". A copy drifts from the first commit, and it already has. Nothing keeps the two in step except remembering to port by hand.

Drift so far (one day)

In Prism Terminal, NOT in Prism:

In Prism, NOT in Prism Terminal (by owner decision, so not drift): split dock, several shells per tab, sidebar/cwd sync and the Set-Location write, "Open terminal here".

Coming next, and each would have to be built twice or ported: dictation (#13, large), help panel (#12), attach (#14).

Options

A. One shared package, consumed by both apps (RECOMMENDED)

A third repo, prism-term-core, holding everything that IS the terminal. Both apps depend on it at a pinned version; a feature is built once and reaches both by a version bump.

  • What goes in: main side (terminal, shells, termPrompt, agentDetect, agentPoll, agentResume, later the dictation engine); renderer side (TerminalPanel, TermFind, link paint, useAgentIndicator, agentTitle, termActivity, agentClock, termTheme, termLook, termAnsi, termPaste, the agent colours, and the TERMINAL SETTINGS SECTIONS as components, so the options are literally the same code in both apps).
  • What stays in each app: its shell. Prism: roots, sidebar, split dock, several shells, cwd sync, app styles. Prism Terminal: cwd tabs, start screen, chrome-from-theme, window lifecycle.
  • The seam: a typed TermHost interface the app's preload implements (spawn / input / resize / kill / clipboard / external links), and a small options object for the places the apps legitimately differ (Prism follows its app style; Prism Terminal drives the chrome from the theme). Differences are declared, not forked.
  • Delivery: an npm dependency pinned to a git tag ("prism-term-core": "github:Maxaubert/prism-term-core#v1.4.0"): no registry needed. It ships TypeScript SOURCE, compiled by each app's own Vite, with Tailwind pointed at it (@source), since both apps share a toolchain. Local iteration with npm link.
  • What keeps them honest: the core has its own unit tests and CI; each app keeps its own e2e; and a PARITY TEST in each app asserts that the terminal options it shows are exactly the list the core exports, so an option can never exist in one app and not the other without a test going red.
  • This is the same idea as the prism-core viewer package Prism's CLAUDE.md already plans to share with Filesmith.
  • Cost, honestly: a real refactor in BOTH apps. Prism is the hard half: its terminal wiring lives inside a 4000-line App.tsx, and Prism is stable and shipping, so it must be done behind Prism's own terminal e2e scenarios, in steps. Several focused PRs, not an afternoon.

B. Keep two copies, add a drift report

A script that diffs the shared files between the two checkouts and lists what differs, run before every PR, plus a checklist line "ported to the sibling? yes / no / not applicable". Cheap and immediate. But every feature is still built or ported twice, the files have already legitimately diverged (theme model, keys) so the report is noisy, and it relies on discipline, which is what has already failed once.

C. One repo for both apps (monorepo)

Both apps and the shared code in one repository. The strongest guarantee, and the biggest upheaval: Prism's repo, CI, releases and history all move. Not recommended; A gets the same result without it.

Recommendation: A, and BEFORE dictation

Dictation is the largest terminal feature yet (engine, model manager, a settings tab, recording UI). Built in Prism Terminal first, it is a large port later. Built in the core, it lands in both apps at once. Suggested order:

  1. Create prism-term-core from PRISM TERMINAL's copy (the newer, cleaner one, already split into small tested files). Prism Terminal switches to consuming it. No behaviour change; its 12 e2e scenarios are the proof.
  2. Prism adopts the core in steps, behind its own terminal e2e, keeping its split dock / several shells / cwd sync as its own code around the core's panel. Prism gains links, the strip fix, the theme-following indicator and the material fix on the way.
  3. Dictation (Dictation when using the agents #13), then attach (Attach a file or folder: pick it, and its path is pasted into the agent #14) and the help panel (Help panel: a searchable catalogue of shell commands #12), are built in the core and ship to both.

Until step 2 is done the honest status is: Prism's terminal is BEHIND Prism Terminal, and B's checklist line is worth adopting in the meantime.

Decisions needed from the owner

  • A, B or C?
  • If A: do the extraction before dictation, or build dictation first and extract after?
  • Which app-shell differences are intentional and stay different (start screen, quit-on-close, Ctrl+T, cog), and which should Prism pick up too?

Activity

  1. Maxaubert commented on Sep 19, 2026

    @Maxaubert
    OwnerAuthor

    Decided (owner, 2026-09-19):

    • Approach: shared code, and THIS repo is the core (owner: 'why can't this repo be the core'). No third repo. Prism Terminal holds the terminal in a core area that Prism consumes at a pinned git tag; Prism Terminal is both the app and the library. Reasons it is better than a separate package: one place, the shared code is always exercised inside a real shipping app and its e2e, and it is simply true (Prism Terminal IS the terminal; Prism embeds it).
      • What that requires, to be settled in the spec: a core boundary that may never import from the app shell (lint-enforced), relative imports only inside it (no @shared / @renderer aliases, which a consumer's bundler cannot resolve), exports on the root package, Prism's electron-vite told to BUNDLE it rather than externalise it (it ships TypeScript source), Tailwind pointed at it with @source, and node-pty / xterm / React kept at identical versions in both apps so npm dedupes them to one copy.
    • Order: the sync work comes first; dictation (Dictation when using the agents #13) waits for it, so dictation is built once.
    • Scope of 'in sync': everything. 'All should be synced essentially, unless it conflicts with one app, then you need to ask me.' So the default is that a terminal behaviour or option exists in both apps; a genuine conflict with one app is raised with the owner, not decided silently.
  2. Maxaubert commented on Sep 19, 2026

    @Maxaubert
    OwnerAuthor

    Status, 2026-09-19 (built while the owner was away; everything is on branches, nothing merged)

    Built and verified

    Found on the way

    • Prism's terminal e2e scenario fails on untouched origin/main: "the original shell retains its scrollback" (a terminal pinned into a split pane). Pre-existing, not caused by the core, not fixed.
    • rowPaste (Prism e2e) timed out once in a full run and passes alone: it uses the real Windows clipboard.
    • One Prism unit test failed once on the untouched baseline and passed on rerun (flaky, not identified).
    • Prism's local main checkout is one release behind origin/main, and Prism Terminal was copied from that older commit. The terminal's own files had barely moved, except termPaste (a copied image FILE should paste as its path), which Prism Terminal never got. The core now carries it.
    • A false claim of mine, fixed in fix: a dropped file types its path; the terminal answers a right-click (#16) #17: a file dropped on the terminal did NOT type its path in Prism Terminal, and the terminal had no right-click menu. Both lived in Prism's split dock and went when I stripped it.

    Decisions only the owner can make

    The core already has a host setting for each of these, so none blocks the PRs above. They decide whether a difference STAYS or goes.

    1. Defaults: one value for both apps, or per app? Today an untouched setting reads differently: theme (style in Prism, prism preset here), acrylic (on / off), indicator (full orange / quiet, theme-coloured). Making them one value changes what existing Prism users see on update. "All synced": does that cover defaults?
    2. The ground painting (A grey strip under the terminal's last row #6's fix). Prism has the same unpainted strip under the terminal's last row, in principle. Adopting the fix means the panel paints and the dock stops; with Prism's acrylic styles that needs a look on real glass. Take it in Prism?
    3. Acrylic and opacity. Here acrylic is a terminal setting that works with any theme, with an opacity slider. In Prism the window material belongs to the app STYLE. Should Prism's terminal get the slider, and who wins when the style is solid?
    4. Indicator colour in Prism: should an unpicked colour follow the APP STYLE's accent (the tab strip is app chrome) or the terminal theme's? And is it fine for the fixed orange and green defaults to go?
    5. Indicator volume is part of a saved theme in Prism, not here. Adopt this app's rule (a theme pick never resets the volume) in Prism?
    6. Close question. Prism has three modes (always / agent / never) and no question when the WINDOW closes; this app has one switch, asks whenever an agent is present, and holds the window while one works. One rule for both?
    7. Key chords. Close tab: Ctrl+W in Prism, Ctrl+Shift+W here (plain Ctrl+W is delete-word in a shell). Ctrl+B and Ctrl+` differ too. One list, or each app its own (as built)?
    8. The agent indicator wiring in Prism's App.tsx. The core has it as useAgentIndicator; adopting it changes two small behaviours in Prism (a finished mark dies when its agent leaves; working is only scored while an agent is present). Take it?
    9. The Settings terminal tab as ONE shared component, so the options are literally the same code. Prism shows it in one "Terminal" tab; this app splits it across General and Appearance. Which layout, or each keeps its own shell round shared sections?
    10. Should the two apps share settings on one machine (pick a theme once), or stay independent as today?
    11. Release cadence. A terminal fix now travels: a PR here, a core-v* tag, a pin-bump PR in Prism, its e2e, install, "merge?". That is two approvals per terminal fix. Acceptable, or should a pin bump that passes Prism's e2e be pre-approved?
    12. Peer session. Step 2 edits main/index.ts, the preload, Settings.tsx and TabStrip.tsx, which PR Agent indicator: restored tabs show Finished after a restart, though nothing finished #151 also touches. Land Agent indicator: restored tabs show Finished after a restart, though nothing finished #151 first and rebase, or the other way round?

    Next, once decided

    The remaining three pieces in Prism (8, 9 and TabStrip), then dictation (#13), attach (#14) and the help panel (#12) are built ONCE, in core/, and reach both apps.

  3. Maxaubert commented on Sep 19, 2026

    @Maxaubert
    OwnerAuthor

    Decided (owner, 2026-09-19):

    • Decision 9, the settings: YES, they belong in the core. 'This should also include settings items. They shouldn't be synced in terms of personalization, but the setting names, types, how they function and so on should be the same.' So the terminal's settings UI becomes shared components in core/, and each app composes its own page round them.
    • Decision 10: personalization is NOT shared between the apps. Each app keeps its own stored values (already true: separate userData).
    • Decision 1, in part: Prism's indicator defaults to MINIMAL, as here (it was Full).
    • Decision 4: Prism's indicator colour defaults to the ACCENT, 'just like in Prism Terminal' (it was a fixed orange). In Prism the accent is the app style's --p-accent, which is what its tab strip already wears; the finished colour follows the same rule as here.

    These change what an existing Prism user sees on update (a quiet accent-coloured line instead of a filled orange tab), knowingly.

  4. Maxaubert commented on Sep 19, 2026

    @Maxaubert
    OwnerAuthor

    Decided (owner, 2026-09-19), the rest of the twelve:

    • 2. Strip fix: the same in both. The panel paints the ground in Prism too (check on real glass with an acrylic style before it merges).
    • 3. Acrylic: Prism keeps ITS meaning, no opacity slider. The style owns the window material; the terminal row stays 'let the style's acrylic show through'. Same row name and type in both apps; what it controls follows the host.
    • 6. Close question: REMOVE THE SETTING, in both apps. Always 'smart': closing a tab whose shell hosts an agent asks; a plain shell closes unasked. No Always / Never, no switch. (Prism's unsaved-text question is a different thing and stays.) The window-close hold while an agent is WORKING applies in both.
    • 7. Shortcuts: Ctrl+W closes a tab in BOTH apps (Ctrl+Shift+W keeps working). Known cost, accepted: plain Ctrl+W no longer deletes a word in the shell; Ctrl+Backspace does.
    • 1 (rest). Theme default stays different: 'follow the app style' only exists where there are app styles. Prism follows its style; Prism Terminal defaults to the Prism preset.
    • 5 and 8. Prism Terminal's indicator rules in both: the core's useAgentIndicator replaces Prism's inline wiring (a finished mark dies when its agent leaves; 'working' is only scored while an agent is detected), and Minimal/Full is never reset by a theme pick nor stored in a saved theme.
    • 11. Cadence: ask 'merge?' both times, AND a terminal fix must run AUTOMATED TESTS confirming Prism's terminal still works before its version-bump PR is offered, 'since it has more failure points due to its larger footprint'. So Prism gets a dedicated terminal gate that every core bump must pass.
    • 12. Prism PR Agent indicator: restored tabs show Finished after a restart, though nothing finished #151: merge it first, then carry on and rebase the terminal work on top.
  5. Maxaubert commented on Sep 19, 2026

    @Maxaubert
    OwnerAuthor

    Progress, 2026-09-19 afternoon. All twelve decisions are applied.

  6. Maxaubert commented on Sep 19, 2026

    @Maxaubert
    OwnerAuthor

    Merged, 2026-09-19: PrismTerminal #17 and #19 (the core; it replaced #18, which GitHub closed when #17's branch was deleted), core-v0.1.0 tagged from main, and Prism #155, #156, #157 (Prism 0.53.0, pinned to core-v0.1.0). Both apps are installed on the owner's machine from merged main. Open: Prism's release workflow is broken by an upstream ffmpeg rotation (Maxaubert/Prism#159 fixes it); the strip fix and the window edge still want a look on real glass. Next: dictation (#13), built once in core/.

  7. Maxaubert commented on Oct 15, 2026

    @Maxaubert
    OwnerAuthor

    Done: the shared core (core/, released from the core-dist branch as core-v* tags, Prism pinned to it) is how both apps stay in sync, with the rules in docs/two-apps.md. Checked and closed 2026-10-15.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions