Skip to content

feat(desktop): desktop shell foundation and project hub (#172)#193

Open
Dencyuman wants to merge 12 commits into
mainfrom
feat/desktop-shell-172
Open

feat(desktop): desktop shell foundation and project hub (#172)#193
Dencyuman wants to merge 12 commits into
mainfrom
feat/desktop-shell-172

Conversation

@Dencyuman

Copy link
Copy Markdown
Contributor

Closes #172 (Phase 1: shell foundation).

What

  • Project hub aligned with the approved mock: brand lockup, centered content column, recents with display-only location labels and 56px rows, always-visible templates section, semibold action buttons in mock order.
  • Bundled Inter + Noto Sans JP variable fonts; token-driven styling throughout (brand/tokens.json → CSS variables only).
  • Full native OS menu: LayerDraw (About / Settings…・disabled / Language with live catalog switching and single-checkmark radio state / Quit), File (New ⌘N / Open ⌘O / Open Recent with internal-ID masking / Close Project ⇧⌘W / Export…・disabled), Edit, View (frontend event bridge), Window, Help.
  • Workspace shell: return-to-hub navigation (breadcrumb and menu), collapsible inspector sections, localized editor/library/viewer/MCP surfaces, internal IDs never rendered.
  • i18n: ~70 new en/ja catalog keys in @layerdraw/react; locale follows the OS with an in-session override from the menu.
  • Backend: recents record display names after commit, location_label threading (dialog and reload open paths), CloseCurrentProject bridge, LAYERDRAW_STATE_ROOT override for isolated QA state.
  • Contracts: docs/desktop-wails-contracts.md documents location_label as hub-display-only.

Verification

  • Go: go build ./..., desktopapp/desktopwails/apps suites green.
  • Frontend: react + desktop unit suites and browser E2E green.
  • Real binary: menu-driven open/close/language flows and hub rendering verified against the approved mock on macOS.

Dencyuman added 12 commits July 22, 2026 09:16
Add a stdlib generator that emits src/tokens.css from brand/tokens.json:
primitives and light-theme semantics at :root, dark and high-contrast
overriding by data-theme attribute. Semantic values resolve to primitive
CSS vars so components reference only semantic tokens. Wire it into the
package generate script (covered by the make generate reproducibility
gate), ship it as a ./tokens.css export, and prepend it to the desktop
frontend stylesheet bundle. Add a drift test asserting the committed file
matches the generator output.
Introduce a dependency-free translator with an I18nProvider/useI18n context,
named-argument interpolation, Intl date/number/relative-time formatting, and a
reason-plus-code error() helper for closed diagnostic codes. Ship a shared
shell catalog with a canonical en base and a complete ja translation (hub,
workspace frame, lifecycle, and diagnostic reasons); locales are plain data so
adding one never touches a component. Export it as ./i18n with a completeness
test asserting ja covers every base key.
…d i18n

Redesign the project hub as a left rail (wordmark + Projects/Library nav) with a
Projects header, primary/secondary actions, a reason-plus-code error banner with
a Show details toggle, and dense recent rows (name + relative time, no internal
identifiers or OS paths). Wrap the shell in an I18nProvider that follows the OS
locale and route every hub, workspace, lifecycle, and error string through the
shared catalog. Give the workspace a topbar with a breadcrumb back to Projects
and a Structure/Views mode switch, keeping the three-pane landmarks intact.
Rewrite the stylesheet to resolve entirely from the generated token variables
(flat elevation, token radii, 13px workbench density) so light/dark/high-contrast
follow the token set with no per-theme overrides. Add a non-throwing
useOptionalI18n so the shell renders with a default translator in tests. Update
the shell, boundary, and e2e tests to the new structure and token-based styling.
Record the Phase 2 path for resolving Wails native-menu labels from the shared
message catalog: the frontend resolves labels through the active translator and
passes already-localized strings across the bridge (Go never owns translation),
matching the diagnostic-code contract and the binding-parity fixtures that gate
adding the binding.
Introduce a Tailwind v4 CLI build step (no bundler required) whose @theme maps
exclusively onto the generated token custom properties — the default Tailwind
palette is never imported, content scanning is restricted to the primitives
module, and the emitted stylesheet contains no raw palette values. Copy in a
minimal shadcn/ui-style primitive set over Base UI (Button, Input, Tabs,
Dialog, DropdownMenu, Select) fully de-shadcn'd: token radii capped at 4/6/8px,
flat elevation via shadow tokens only, 13px workbench density, semantic token
colors throughout. Ship as ./primitives with ./primitives.css and tests
asserting the token-only theme and closed-overlay behavior.
…icon

Generate src/brand-assets.ts from brand/png (committed output, regenerated via
the package generate script) so Desktop chrome renders the canonical LayerDraw
icon instead of a hand-drawn substitute, and rebuild the wordmark row as
icon-plus-ink-text per the brand kit. Replace the Wails default
build/appicon.png with brand/png/layerdraw-icon-1024.png so the packaged app's
Dock and Finder icon carry the LayerDraw brand, and ignore the generated
build/bin and build/darwin outputs.
Rebuild the hub to the approved layout: 224px rail on the app background with
brand row, icon nav, and a live MCP status chip; inline page header actions on
the shared Button primitive; a danger banner with icon, bold reason, mono
stable code, Show details, and dismiss; Recent as one bordered container with
subtle dividers and dense rows (brand file icon, 500-weight name, mono
location line when the host provides one, tabular relative time, chevron,
canvas hover). Legacy recents whose stored name is a raw internal identifier
render the localized untitled label — internal IDs never reach primary UI.
Template cards render only when a connected Library source exists, from real
Registry template results plus a dashed blank-project card, replacing the
unstyled hub Library panel. The workspace mode switch now uses the shared Tabs
primitive, and the frontend stylesheet bundle includes the emitted primitives
layer.
The owner removed the rail-footer MCP chip from the approved mock as
contextless; MCP status surfaces through the View menu and the Settings
screen instead. Remove the chip, its styles, its status lookup, and the
now-unused catalog entries; the rail footer stays empty.
Remove the UA body margin so the shell owns the window, fix the primary
button foreground losing its specificity battle against the shell
button reset, replace the icon-plus-text brand row with the canonical
logo lockups (light/dark), compose the app icon into the macOS 1024/824
grid so the system stops shrinking it, strip internal identifiers and
state-machine readouts from the workspace (abnormal statuses surface as
localized chips), fold the Library/Review/AI panels into localized
inspector sections without raw session identifiers, localize the
remaining workspace strings (en/ja), and record the committed project
name into recents instead of the pre-commit bootstrap title.
Adds the full native menu (LayerDraw with live Language switching, File
with Open Recent and Close Project, Edit, View commands over a frontend
event bridge, Window, Help), threads a display-only location_label
through recents on both the dialog and reload open paths, masks internal
document IDs in the Open Recent menu, and documents the label as
hub-display-only in the desktop contracts.
Centers the hub content column in the main pane, enlarges the brand
lockup, keeps exactly one Language checkmark by rebuilding the menu from
tracked state on selection, and adds a LAYERDRAW_STATE_ROOT override so
QA runs stop writing into the production recents store.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Builds the Phase 1 Desktop shell foundation: introduces token-driven styling + shared UI primitives, adds a message-catalog i18n layer with runtime locale switching, and expands the Wails desktop menu / project lifecycle plumbing to support hub ⇄ workspace navigation without restarting.

Changes:

  • Add @layerdraw/react i18n + Base UI–backed primitives (Tailwind v4 theme mapped to generated token CSS) with tests to prevent shadcn defaults and stale generated outputs.
  • Implement the Desktop hub + three-pane workspace shell styling and wiring (recents display rules, templates section, inspector sections, locale switching).
  • Extend the Go/Wails backend + contracts for recents display metadata (location_label) and project close/navigation, plus native menu scaffolding.

Reviewed changes

Copilot reviewed 53 out of 63 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Workspace-level pnpm build policy adjustment.
packages/react/test/tokens-css.test.mjs Tests ensuring generated tokens.css stays in sync and enforces token invariants.
packages/react/test/primitives.test.mjs Tests for primitives exports + emitted CSS constraints (no shadcn palette bleed).
packages/react/test/package-boundary.test.mjs Updates export-surface expectations for new i18n/primitives/tokens entries.
packages/react/test/i18n.test.mjs Unit tests for catalog completeness, locale resolution, interpolation, formatting, and provider behavior.
packages/react/src/tokens.css Generated token custom properties for light/dark/high-contrast themes.
packages/react/src/primitives/tabs.ts Base UI Tabs wrapper restyled to tokenized segmented control.
packages/react/src/primitives/select.ts Base UI Select wrapper restyled with token-driven classes.
packages/react/src/primitives/menu.ts Base UI Menu wrapper restyled with token-driven classes.
packages/react/src/primitives/input.ts Base UI Input wrapper restyled with token-driven classes.
packages/react/src/primitives/index.ts Barrel export for internal primitives module.
packages/react/src/primitives/dialog.ts Base UI Dialog wrapper restyled with token-driven classes.
packages/react/src/primitives/cn.ts Minimal cn() helper to avoid runtime deps.
packages/react/src/primitives/button.ts Base UI Button wrapper with tokenized variants/sizes.
packages/react/src/primitives.css Tailwind v4 theme mapping to generated token variables + utilities import.
packages/react/src/index.ts Exposes i18n via the package root entrypoint.
packages/react/src/i18n.ts New dependency-free i18n catalog system + shared shell catalogs (en/ja).
packages/react/scripts/generate-tokens-css.mjs Generator producing src/tokens.css from brand/tokens.json.
packages/react/package.json Adds Base UI + Tailwind CLI build outputs and exports tokens/primitives CSS artifacts.
internal/desktopwails/run.go Native menu expansion (Language switching, recents, close project, view/panel events).
internal/desktopwails/run_test.go Adjusts native menu test for new structure and labels.
internal/desktopwails/frontend.go Adds CloseCurrentProject bridge method for hub navigation.
internal/desktopapp/projects.go Threads committed display name into recents + computes location_label.
internal/desktopapp/project_lifecycle.go Persists/returns location_label in recent project records.
internal/desktopapp/project_lifecycle_test.go Updates lifecycle tests for new opened(..., locationLabel) signature.
internal/desktopapp/project_lifecycle_api.go Exposes ActiveSessions() for shell close behavior.
go.sum Dependency graph reshaping from updated module usage.
docs/desktop-wails-contracts.md Documents location_label as display-only hub metadata.
docs/desktop-native-menu-i18n.md Design memo for future native-menu localization bridge.
apps/desktop/test/shell.test.mjs Updates shell tests for hub title/actions ordering and recents ID-masking + location/time display.
apps/desktop/test/package-boundary.test.mjs Updates desktop styles test to assert token-variable layout widths.
apps/desktop/test/library-panel.test.mjs Updates tests for localized/renamed library form labels.
apps/desktop/test/browser-e2e.mjs Loads tokens/primitives CSS in E2E and updates selectors/labels for new UI.
apps/desktop/src/wails-bootstrap.ts Wails composition updates including CloseCurrentProject integration.
apps/desktop/src/viewer-surface.ts Viewer empty/attention strings now use i18n (partial).
apps/desktop/src/styles.css Large rewrite to token-driven desktop shell styling + bundled fonts.
apps/desktop/src/shell.ts New hub + workspace shell structure, i18n integration, recents/templates rendering, inspector sections.
apps/desktop/src/mount.ts Wraps shell in I18nProvider and adds runtime locale override API.
apps/desktop/src/mcp-panel.ts MCP panel strings moved to shared i18n and updates formatting/scopes labels.
apps/desktop/src/library-panel.ts Library panel strings moved to shared i18n.
apps/desktop/src/editor-surface.ts Editor toolbar strings moved to shared i18n; removes non-user-actionable state summary.
apps/desktop/src/contracts.ts Adds projectDialogs.close() to support return-to-hub navigation.
apps/desktop/src/brand.ts Adds brand icon/wordmark components using generated embedded assets.
apps/desktop/scripts/normalize-wails-bindings.mjs Ensures SPDX headers and normalizes generated Wails bindings; now includes ShellBinding.
apps/desktop/scripts/generate-brand-assets.mjs Generates brand-assets.ts from canonical brand/ assets.
apps/desktop/scripts/build-frontend.mjs Bundles fonts and concatenates tokens/primitives/editor/desktop CSS into app bundle.
apps/desktop/package.json Adds generate script for brand assets and keeps build pipeline consistent.
apps/desktop/main.go Adds LAYERDRAW_STATE_ROOT override with absolute-clean-path validation.
apps/desktop/frontend/wailsjs/runtime/runtime.js Regenerated Wails runtime JS artifact (header consistency issue noted in comments).
apps/desktop/frontend/wailsjs/runtime/runtime.d.ts Regenerated Wails runtime d.ts artifact (header consistency issue noted in comments).
apps/desktop/frontend/wailsjs/go/models.ts Updates generated Go model bindings for new types/fields (location_label, close result).
apps/desktop/frontend/wailsjs/go/desktopwails/ShellBinding.js New generated ShellBinding JS bridge artifact.
apps/desktop/frontend/wailsjs/go/desktopwails/ShellBinding.d.ts New generated ShellBinding d.ts bridge artifact.
apps/desktop/frontend/wailsjs/go/desktopwails/FrontendBridge.js Adds CloseCurrentProject binding export.
apps/desktop/frontend/wailsjs/go/desktopwails/FrontendBridge.d.ts Adds CloseCurrentProject binding type.
apps/desktop/frontend/src/main.ts Wires native menu events to locale switching + menu command bridge; return-to-hub via close binding.
apps/desktop/.gitignore Ignores desktop build output directories.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

},
};

/** Assert that every non-base locale defines exactly the base locale's keys. */
OpenProjectDialog(requestID: string): Promise<DesktopHostResult<DesktopProjectOpenDTO>>;
RecentProjects(): Promise<DesktopHostResult<readonly DesktopRecentProjectDTO[]>>;
OpenRecentProject(projectID: string): Promise<DesktopHostResult<DesktopProjectOpenDTO>>;
CloseCurrentProject(): Promise<{ readonly outcome: string }>;
Comment on lines +222 to +226
close: async () => {
const result = await application.CloseCurrentProject();
await lifecycle.refreshPublication();
return result as never;
},
Comment thread apps/desktop/src/shell.ts
}, [hubVisible, projectDialogs, dialogPending]);

const failure = state.failure === undefined ? null : suppliedLabels.failure[state.failure.message_key];
const failure = state.failure === undefined ? null : t.t(`error.${state.failure.message_key}`);
Comment thread apps/desktop/src/shell.ts
library === undefined ? null : inspectorSection(t.t("inspector.section.library"), false, createElement(DesktopLibraryPanel, { library, project: project.library_project })),
reviewModel === undefined ? null : inspectorSection(t.t("inspector.section.review"), false, createElement(ReviewPanel, { model: reviewModel })),
inspectorSection(t.t("inspector.section.mcp"), false, createElement(DesktopMCPPanel, { mcp: mcp ?? unavailableMCP, projectID: project.project_id })))),
createElement("div", { className: "ld-desktop-visually-hidden", role: "status", "aria-live": "polite", "aria-atomic": true }, state.pending_action === "select_view" ? "Opening view…" : state.pending_action === "review_recovery" ? "Opening recovery options…" : state.pending_action === "disconnect_storage" ? "Disconnecting external storage…" : ""));
Comment on lines 80 to 86
if (state.status === "loading" || state.status === "cancelling") return createElement("p", { role: "status", "aria-live": "polite" }, "Loading view…");
if (state.status === "empty") return createElement("p", { className: "ld-desktop-empty" }, state.reason === "view_empty" ? "This view is empty." : "Select a view to begin.");
if (state.status === "empty") return createElement("p", { className: "ld-desktop-empty" }, s_translator.t(state.reason === "view_empty" ? "workspace.empty.view" : "workspace.empty.select"));
if (state.status === "disposed") return createElement("p", { role: "status" }, "Viewer closed.");
const publication = "publication" in state ? state.publication : state.previous;
return createElement("div", { className: "ld-desktop-viewer", "data-viewer-state": state.status },
publication?.render_data.kind !== "diagram" ? null : createElement("div", { className: "ld-desktop-view-mode", role: "toolbar", "aria-label": "Viewer dimension" },
createElement("button", { type: "button", "data-mode": "2d", "aria-pressed": mode === "2d", onClick: () => setMode("2d") }, "2D"),
Comment on lines +303 to +309
appMenu := result.AddSubmenu("LayerDraw")
appMenu.AddText("About LayerDraw", nil, func(*menu.CallbackData) {
_, _ = wailsruntime.MessageDialog(bridge.context(), wailsruntime.MessageDialogOptions{
Type: wailsruntime.InfoDialog,
Title: "LayerDraw",
Message: "LayerDraw Desktop\nTyped-graph modeling for software architecture.",
})
@@ -1,5 +1,3 @@
// SPDX-License-Identifier: LicenseRef-LayerDraw-1.0

/*
@Dencyuman Dencyuman closed this Jul 22, 2026
@github-project-automation github-project-automation Bot moved this from Triage to Done in LayerDraw Development Jul 22, 2026
@Dencyuman
Dencyuman deleted the feat/desktop-shell-172 branch July 22, 2026 00:23
@Dencyuman
Dencyuman restored the feat/desktop-shell-172 branch July 22, 2026 00:26
@Dencyuman Dencyuman reopened this Jul 22, 2026
@github-project-automation github-project-automation Bot moved this from Done to Triage in LayerDraw Development Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

[Feature]: Rebuild the Desktop shell into a navigable three-pane layout

2 participants