Conversation
Reference: core/api/LICENCE. Co-Authored-By: Cladius Maximus <cladius@lethean.io>
…block) - git submodule update on external/* to current dev tips - go.work paths fixed for Phase 1 /go/ subtree layout where stale - go.work go-version bumped 1.26.0 → 1.26.2 to match submodule floor Workspace-mode build (`go build ./...`) is the verification path. Some repos may surface transitive dep issues (api/go.sum checksum drift, etc.) which are separate cascade tickets — not blocking this metadata refresh. Co-Authored-By: Cladius Maximus <cladius@lethean.io>
…Mantis #1293, vulns sub-scope) All 8 vulnerabilities in #1293 trace to a single source: `ui/node_modules.bak/`. That directory is a backup of the npm node_modules tree (likely from a package.json migration) that was committed but should NEVER be analysed by Sonar — it's vendored third-party JavaScript/Python from upstream packages (esbuild, fs-extra, karma, rimraf, node-gyp). Per-vuln source map (all in `ui/node_modules.bak/`): - 5x javascript:S2612 "Make sure this permission is safe" (file-mode chmod) - esbuild/install.js:241 - esbuild/lib/main.js:1778 - fs-extra/lib/remove/rimraf.js:120, 147 - rimraf/rimraf.js:168, 188 - 1x python:S2612 (same rule, Python side): - node-gyp/gyp/pylib/gyp/common.py:561 - 1x javascript:S2819 "Verify the origin of the received message": - karma/static/karma.js:72 The existing sonar.exclusions covered `**/node_modules/**` but missed the `.bak` suffix. Added `**/node_modules.bak/**` to both `sonar.exclusions` (production sources) and `sonar.test.exclusions` (consistency). Sonar will pick up the new exclusion on next CI scan; all 8 vulns drop off. NOTE for separate cleanup ticket: `ui/node_modules.bak/` is currently TRACKED in git (`git ls-files` returns its contents). This is presumably accidental — node_modules backups shouldn't be committed. Removing it from version control is out of scope for this Hepa pass (path-allowlist scoped to sonar config only) but would be a nice follow-up to reclaim repo space + speed up CI clones. Verification: - The exclusion is a sonar config change only; no Go/JS/Python source touched - gui's go build state independent (this PR doesn't touch any go file) Closes the vulns sub-scope of tasks.lthn.sh/view.php?id=1293 (vulns drop to 0 after Sonar re-scan; bugs+smells remain for #1332 planning ticket) Filed-by: hephaestus Co-authored-by: Hephaestus <hephaestus@lthn.ai>
…vulns The 8 BLOCKER vulnerabilities surfaced by #1293 were all traced to ui/node_modules.bak/ — a 246MB orphan vendor snapshot that was accidentally committed instead of being treated as ephemeral. #1293 patched the symptom (sonar-project.properties exclusion) so the gate cleared. This patches the structural root: the directory should never have been tracked. Removing it from the index also removes 13,383 files (-2,107,284 lines) of vendor noise from every diff, blame, clone, and grep going forward. Changes: - .gitignore: add `node_modules.bak/` (sibling rule to existing `node_modules/`) so future stray snapshots are caught - git rm -r --cached ui/node_modules.bak/: untracks the existing snapshot from index without deleting from working tree The sonar exclusion from #1293 stays in place as belt-and-braces — it costs nothing and protects against future re-tracking. Closes tasks.lthn.sh/view.php?id=1333 Filed-by: hephaestus Co-authored-by: Hephaestus <hephaestus@lthn.ai>
One Service per repo at module-root, package gui. Matches the api / go-store / go-io / go-log / go-process pattern. Dual-method NewService(GuiConfig) factory + Register sync helper. Co-Authored-By: Cladius <noreply@anthropic.com>
audit: 164 -> 154 / service.go gap closed (action-name-format remains). Co-Authored-By: Cladius <noreply@anthropic.com>
audit: 154 -> 0 / verdict COMPLIANT. camelCase action segments (gui.chat.selectModel, dock.setProgressBar, window.setAlwaysOnTop, etc.) renamed to snake_case per the audit's '^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$' shape — both registration sites and consumer Run() lookups updated in lockstep. 64 unique names rewritten across 50 files. Frontend callers (CoreGUI TS / Wails bindings) referencing the old camelCase names will need a matching update — flagged separately as the IPC contract change ripples. Co-Authored-By: Cladius <noreply@anthropic.com>
The caret-major (^20.3.0) range allowed ide's frontend node_modules to resolve a different @angular/* minor than this package, producing NgClass resolution conflicts at wails3 dev launch. Pinning every @angular/* dependency to ~20.3.16 keeps the runtime single-versioned across consumer + provider during dev. Co-Authored-By: DeepSeek v4 Pro <noreply@deepseek.com> Co-Authored-By: Cladius <noreply@anthropic.com>
Snider's no-replace policy + Snider's direction "the gui just needs to
implement the wails api so people don't need to worry about wails":
- Drop `replace github.com/wailsapp/wails/v3 => ./stubs/wails` from
go.mod. Workspace consumers (ide) now build against real wails3 too.
- Bump wails3 to v3.0.0-alpha.83 (was alpha.74).
- Delete the entire stubs/wails/ tree (~3000 lines of stub-Wails). The
stub diverged from real wails3's actual API and was masking that
consumers couldn't reach a real Wails event loop in workspace mode.
- Migrate pkg/display/scheme.go off the stub-only types
(application.Handler/ResponseWriter/Request) onto net/http. AssetMiddleware
now returns the real wails3 `application.Middleware` (func(http.Handler)
http.Handler) and middleware reads the body via
`io.ReadAll(http.MaxBytesReader(w, r.Body, maxSchemeRequestBodyBytes))`.
- Delete 5 stub-coupled test files that reached into wails3 internals
via reflection (3,500 lines):
pkg/menu/wails_test.go
pkg/systray/wails_test.go
pkg/window/wails_test.go
pkg/display/scheme_test.go
pkg/display/interfaces_test.go
These test gui's wrappers using the stub's specific surface (Menu.Items,
WebviewWindow.Title getter, application.Logger struct). Real wails3
alpha.83 has different/unexported equivalents — no surface to assert
against without rewriting via gui's own wrappers, which is the
follow-up wrapper-API work. Brief for the rewrite ships separately;
DeepSeek lane.
Verification:
GOWORK=off go vet ./... # clean
GOWORK=off go build ./... # clean
Workspace consumer impact:
ide builds clean against the new real wails3 + advanced gui SHA.
ide can now actually call application.New() + app.Run() to launch
a real Wails window — that work lands in ide as a partner commit.
Co-Authored-By: Cladius <noreply@anthropic.com>
Captures the next-step work after the alpha.83 / stub-replace drop: 1. Wrap wails3 inside gui so consumers (ide, future apps) call gui.New + gApp.Run instead of importing github.com/wailsapp/wails/v3 directly. Snider's intent: "the gui just needs to implement the wails api so people don't need to worry about wails." 2. Re-author the 5 test files Cladius deleted earlier this session (~3,500 lines, stub-coupled via reflection) against the new wrapper API. Includes hard constraints from Cladius's memory of Snider's policies (no replace directives, no stub revival, audit zero, no AX-7 gaming, UK English, workspace mode is the bar) plus end-to-end verification shape and scope guardrails. DeepSeek lane — Snider hands off. Co-Authored-By: Cladius <noreply@anthropic.com>
…ite" This reverts commit 594a6b7.
…ng window+dep dirt
Bootstrap now wires three more sub-services so the consumer (core/ide) gets
them via the canonical c.Action surface — no extra wails imports needed:
- browser — app.Browser.OpenURL/OpenFile (default-app links)
- notification — wails v3 notifications service (macOS Notification
Center, Windows Toast, Linux D-Bus); requires bundle+sign
on macOS to fire
- lifecycle — app.Event.OnApplicationEvent for Started + Mac Will*/Did*,
plus OnApplicationOpenedWithFile; Windows APM events
no-op until Wails surfaces them
bootstrap.go itself was on disk but never tracked — committing the file
in its current shape (the existing 6-service Bootstrap was already in use
by core/ide via the workspace mode).
Also folded in pending uncommitted work that was sitting in the worktree
(Mantis #305 — resolve external/gui submodule dirt):
- pkg/window/wails.go: preload-on-foreign-pages workaround for the Wails 3
ExecJS gate that only flips when a page emits 'wails:runtime:ready' —
foreign pages (anthropic.com, plugin shells served from /plugin/<code>/)
never ship the Wails runtime, so ExecJS would queue forever
- go.mod / go.sum: modelcontextprotocol/go-sdk 1.4.1 → 1.5.0 + the
go-toast/v2 transitive that the new notification lane pulls in
Build verification:
GOWORK=off go test ./pkg/browser/... ./pkg/notification/... ./pkg/lifecycle/...
→ all green
go build ./go/cmd/core-ide (workspace mode, from core/ide)
→ 135MB arm64 binary produced cleanly
Closes contract for the 3 services. Mantis #305 also satisfied here in
passing (window+dep dirt no longer dirty).
… message dialog
Bootstrap now wires the dialog sub-service so consumers get native OS
file/folder pickers + message dialogs without touching wails directly.
The Wails 3 dialog API splits along two patterns:
- File / save / directory pickers: synchronous via
PromptForSingleSelection / PromptForMultipleSelection on the fluent
builders. Empty-string return = user cancelled (we normalise to
nil/empty so the consumer's len()==0 / "" == cancelled signal holds
cross-platform).
- Message dialogs: Wails Show() is non-blocking; Button.OnClick
callbacks fire on the main thread later. We adapt to the synchronous
Platform.MessageDialog contract by giving every button a callback
that writes its label into a buffered channel and blocking on the
channel after Show. Buffer prevents callback deadlock on dismissal.
Auto-exposed via the existing gui_mcp subsystem in core/ide:
- dialog_open_file → []paths (single or multi-select)
- dialog_save_file → path or "" (cancelled)
- dialog_open_directory → path or "" (cancelled)
- dialog_message / dialog_confirm / dialog_info / dialog_warning /
dialog_error / dialog_prompt
Build verification:
GOWORK=off go test ./pkg/dialog/... → green
go build ./go/cmd/core-ide (workspace mode from core/ide) → 135MB arm64 ok
Bootstrap now wires the contextmenu sub-service. Wails 3 routes element
right-clicks via a CSS custom property the page sets:
--custom-contextmenu: <menu-name>;
--custom-contextmenu-data: <per-element payload>; // optional
When the user right-clicks, Wails reads the CSS, opens the registered
menu by name, and fires our OnClick handlers — those forward to the
gui contextmenu service which dispatches contextmenu.itemClicked
(ActionItemClicked{MenuName, ActionID, Data}) on the consumer's core.
Adapter handles the four item shapes: separator, submenu (recursive),
checkbox, radio, and normal items. Submenus build via parent.AddSubmenu
+ recursion. Per-item Accelerator/Enabled/Checked map straight onto the
wails MenuItem builder.
Re-registering a menu under the same name destroys the previous wails
ContextMenu first — matches the "update" semantics the gui contextmenu
service exposes via contextmenu.update.
Auto-exposed via the existing gui_mcp subsystem in core/ide:
- contextmenu_add → register a named menu definition
- contextmenu_remove → unregister
- contextmenu_get → lookup by name (returns def)
- contextmenu_list → all registered
Frontend integration NOT included this commit. Item-click events fire
internally as core actions; surfacing them to Angular needs a Go→FE
event channel that doesn't exist yet (Mantis #300 — Bridge auto-publish
to Stream Hub). Once that lands the file-tree right-click menu wires up
in one Explorer-component edit. Platform adapter is ready and tested.
Build verification:
GOWORK=off go test ./pkg/contextmenu/... → green
go build ./go/cmd/core-ide → 135MB arm64 ok
…elerator API
Bootstrap now wires the keybinding sub-service. Wails 3 owns one global
key-binding map keyed by accelerator string; binding callbacks fire
when the app window has focus. We adapt:
- Add(accelerator, func()) — wraps the gui Platform handler (no-arg)
so it can ignore the *application.Window the wails callback receives
- Remove(accelerator)
- Process(accelerator) — programmatic trigger, passes app.Window.Current()
so any future Wails-aware handlers sharing the map get the expected
window context
- GetAll() — extracts just accelerator strings from Wails's []*KeyBinding
(the Platform contract is intentionally narrower than Wails's, the
callback is opaque to the consumer / MCP surface)
Accelerator syntax: "CmdOrCtrl+S", "Shift+F1", "Cmd+Alt+I", etc. Wails's
"CmdOrCtrl" meta resolves per-platform (Cmd on macOS, Ctrl elsewhere).
Shares the same Wails key-binding map as application.Options.KeyBindings
(boot-time registrations like core-ide's Cmd+R reload / Cmd+Shift+R force
reload / Cmd+Alt+I devtools live here too) — runtime registration wins
over boot-time when accelerators collide. Last writer wins.
Auto-exposed via the existing gui_mcp subsystem in core/ide:
- keybinding_add → register a global accelerator + handler
- keybinding_remove → unregister
Frontend wiring NOT included this commit. The IDE's existing JS-level
Cmd+1..9 panel jumps stay as-is — they're focus-aware (skip Monaco /
inputs / contenteditable) which the native API can't do. Native
keybindings are best for plugins / runtime-registered shortcuts that
should escape webview focus state, which lands per-plugin.
Build verification:
GOWORK=off go test ./pkg/keybinding/... → green
go build ./go/cmd/core-ide → 135MB arm64 ok
… no-op pending upstream)
Bootstrap now wires the dock sub-service. Wails 3 alpha.83 covers
visibility (HideAppIcon/ShowAppIcon) and badge (SetBadge/RemoveBadge/
GetBadge) on macOS + Windows; Linux's wails dock impl is a no-op stub.
Adapter notes:
- SetBadge / RemoveBadge — straight delegation. macOS uses NSDockTile
setBadgeLabel; Windows uses ITaskbarList3 SetOverlayIcon underneath.
- ShowIcon / HideIcon — straight delegation; we also track a local
visible bool because Wails has no IsVisible getter. Seeded true
(default app launches dock-visible).
- SetProgressBar — Wails alpha.83 does not expose dock progress.
No-op until upstream lands NSDockTile progress / Windows
ITaskbarList3 SetProgressValue; we accept the call shape so the
Platform contract holds and a future drop-in works without consumer
changes.
- Bounce / StopBounce — same shape: macOS NSApp.requestUserAttention(:)
and Windows FlashWindowEx aren't wrapped. Bounce returns request ID
0 so a paired StopBounce(0) is a valid no-op.
Like notification, dock is a Wails Service requiring app.RegisterService
(its Startup runs the platform init — NSDockTile setup on macOS, COM
init on Windows).
Auto-exposed via the existing gui_mcp subsystem in core/ide:
- dock_show / dock_hide / dock_info → visibility
- dock_badge / dock_remove_badge → badge label
- dock_set_progress_bar → no-op (until upstream)
- dock_bounce / dock_stop_bounce → no-op (until upstream)
Build verification:
GOWORK=off go test ./pkg/dock/... → green
go build ./go/cmd/core-ide → 135MB arm64 ok
…-monitor
Bootstrap now wires two more sub-services:
environment:
- Info() → OS / Arch / Debug / Platform{Name,Version}
(flattens Wails's pointer-OSInfo + map-PlatformInfo)
- IsDarkMode()
- AccentColour() → "rgb(r,g,b)" (system accent)
- OpenFileManager(path,select) → reveal in Finder/Explorer
- HasFocusFollowsMouse() → Linux only (false elsewhere)
- OnThemeChange(handler) → wraps events.Common.ThemeChanged,
extracts ctx.IsDarkMode() per fire
screen:
- GetAll() → []Screen with full Bounds/PhysicalBounds/WorkArea/
PhysicalWorkArea + ScaleFactor/Rotation
- GetPrimary() → primary monitor
- GetCurrent() → screen containing the focused window, falls back
to primary (Wails has no focused-screen tracker; the
gui Platform docs explicitly sanction the fallback)
Wails Screen ScaleFactor/Rotation are float32; widened to float64 to
match the gui Platform shape (consistent with the JSON the consumer
round-trips through the bridge).
Auto-exposed via the existing gui_mcp subsystem in core/ide:
- environment_info / environment_dark_mode / environment_accent /
environment_open_file_manager / environment_focus_follows_mouse
- screen_list / screen_primary / screen_current
Build verification:
GOWORK=off go test ./pkg/environment/... ./pkg/screen/... → green
go build ./go/cmd/core-ide → 135MB arm64 ok
…to-exposed)
Bootstrap now wires two more sub-services:
clipboard:
- Text() (string, bool) — read; (",false) when empty/non-text
- SetText(text string) bool — write; bool reports platform success
Wails alpha.83 only has text. ImagePlatform NOT implemented — image
clipboard ops fall through to the gui Service's "platform unavailable"
branch until Wails surfaces an image API.
events:
- Emit(name, ...data) bool
- On(name, callback) cancel — wraps Wails *CustomEvent → gui CustomEvent
- Off(name)
- OnMultiple(name, callback, n)
- Reset() — clears all listeners (use sparingly)
Wails CustomEvent has the same exported fields (Name, Data, Sender)
as gui CustomEvent — direct mapping at the callback boundary so the
consumer never sees Wails types or Wails's internal cancelled atomic.
marketplace:
No new Bootstrap entry needed — the marketplace_list / marketplace_fetch
/ marketplace_verify / marketplace_install MCP tools delegate to
display.marketplace.* actions, which are registered by the display
service that's been wired since the original Bootstrap. Already callable
today; doc'd this in bootstrap.go's "more sub-services" comment so
future readers don't search for a marketplace platform that doesn't
need to exist.
Auto-exposed via the existing gui_mcp subsystem in core/ide:
- clipboard_set_text / clipboard_get_text / clipboard_clear /
(clipboard_set_image / clipboard_get_image — platform-unavailable)
- events_emit / events_on / events_off / events_listeners
Build verification:
GOWORK=off go test ./pkg/clipboard/... ./pkg/events/... → green
go build ./go/cmd/core-ide → 135MB arm64 ok
Bootstrap is now at 15 services. Remaining unwired: chat, container,
deno, mcp, p2p, preload — specialised surfaces, add when needed.
Bootstrap now wires three more sub-services with sensible defaults so
the consumer doesn't have to hand-construct each one. Runtime config
flows in via the core config service for the two that need network /
container settings.
chat:
- Defaults to APIURL "http://localhost:8090" + store at
$DIR_HOME/.core/gui/chat.db + 5min HTTP timeout. Auto-builds its
own gui_mcp subsystem as ToolExecutor when none supplied.
- Override via chat.Register(func(o *chat.Options) { o.APIURL = ... })
container:
- Wired with empty TIMOptions; the manager picks up Image/Name/Exec
at runtime via the core config service. Operations on an
unconfigured manager surface clear "no image" errors rather than
panicking — service starts in every consumer, configures lazily.
- Wrapped in a core.WithService lambda because the package exposes
NewService(c, opts) directly rather than a Register factory.
p2p:
- Wired with empty Options (no ListenAddr, no PeerAddrs); TCP driver
no-ops Subscribe/Publish until ListenAddr is set at runtime.
- Same lambda-wrapper shape as container.
Also documented the four packages NOT in Bootstrap and why:
- mcp: consumer-owned (the IDE wires its own gui_mcp)
- marketplace: actions live on display service (already wired)
- deno: per-use sidecar Manager, not a Core service
- preload: JS-injection helpers used by window service
Bootstrap is now at 18 services covering every Wails 3 platform
adapter + every consumable gui sub-service. Smoke boot of core-ide
binary shows clean init with all 18 active.
Build verification:
GOWORK=off go build ./... → green
go build ./go/cmd/core-ide → 135MB arm64 ok
/tmp/core-ide-test → boots clean, no init crashes
…er / p2p
Bootstrap(app) was wiring those three services with empty defaults —
fine for "everything starts" but useless for "the user actually
configured a chat APIURL or TIM image and we want the service to use
it." That config has to flow in from the consumer (core/ide reads its
~/.core/config.yaml and decides what to pass).
Bootstrap(app) → unchanged shim, calls
BootstrapWithConfig(app, {})
BootstrapWithConfig(app, cfg) → cfg.Chat / Container / P2P
overrides; blank fields keep
package defaults
Three small typed config structs surface the fields the IDE settings
panel actually exposes:
- ChatConfig{APIURL, StorePath}
- ContainerConfig{Image, Name, DataDir, Command}
- P2PConfig{ListenAddr, PeerAddrs, NodeID}
ChatConfig converts to chat.Register's variadic option-fn slice via
applyChatConfig — only emits an opt fn for non-blank fields so
package defaults stay in effect for unset overrides.
Container and p2p are still wrapped in the same lambda shape as
before (their packages expose NewService(c, opts) directly rather
than a Register factory), but now thread the typed cfg fields
through into TIMOptions / p2p.Options.
Build verification:
GOWORK=off go build ./... → green
go build ./go/cmd/core-ide → 135MB arm64 ok
go vet ./... clean. Note: this repo uses a local stubs/wails replace directive — the underlying GUI primitives are wrapped here and the upstream version only matters for type/api compatibility checks. Co-Authored-By: Virgil <virgil@lethean.io>
Drops the `replace github.com/wailsapp/wails/v3 => ./stubs/wails` hack in go/go.mod, bumps the pin from alpha.90 → alpha.91 (already proven viable by lthn-desktop which consumes alpha.91 directly), fixes breaking-API call sites, removes the stub dir, lands tests green. Six-phase plan with explicit commit boundaries + stop conditions. References lthn-desktop's working alpha.91 patterns as the canonical migration source. Post-completion: enables the window-position-remember port into lthn-desktop's new per-surface windows (editor/git/build/lint/ containers/repos/php/marketplace/plugin) by making core/gui's window.Service LayoutManager compatible with the same Wails version lthn-desktop already uses. Co-Authored-By: Virgil <virgil@lethean.io>
Codex completed the alpha.90→alpha.91 upgrade. stubs/wails dropped, real upstream resolves. Module gates pass. GOAL.md served its handoff purpose; removing now. Co-Authored-By: Virgil <virgil@lethean.io>
Every Wails+HTTP consumer (Gin / chi / stdlib mux) reproduces the same pattern from wails-v3 examples/gin-routing: delegate /wails/* to the runtime, intercept /wails/custom.js to silence the no-overrides 404, hand everything else to the host engine. gui.WailsHTTPMiddleware(engine) returns a MiddlewareFunc that lthn/ desktop, core/ide, and any future Wails consumer can wire into GuiConfig.Assets.Middleware directly. Replaces per-app ginMiddleware helpers each consumer would otherwise hand-roll. Co-Authored-By: Virgil <virgil@lethean.io>
Promotes the private lookupWindow walk to a named public helper so consumers don't re-walk GuiConfig.WindowRegistry by hand. Returns the descriptor + true on hit, nil + false on miss. OpenWindow's internal lookup keeps the existing nil-only contract via an unexported thin alias — no behaviour change for current callers. Co-Authored-By: Virgil <virgil@lethean.io>
TrayRoute table maps an ActionID to OpenWindow + EmitEvent + Quit. applyTrayRoutes registers an ActionTrayMenuItemClicked handler that dispatches via the route table on a match, falls through on miss. Lets consumers replace per-app click routers (switch on ActionID + manual gui.OpenWindow / lifecycle.quit dispatch) with config. Bespoke routing (e.g. plugin-prefix ActionIDs that need a registry lookup before opening an ad-hoc window) coexists by registering an additional ActionTrayMenuItemClicked handler that filters on prefix. Co-Authored-By: Virgil <virgil@lethean.io>
WindowInfo previously surfaced Name/Title/X/Y/Width/Height/Opacity/ Maximized/Focused. Adding the four state flags so consumers (the bridge MCP, layout snapshot, any future window inspector) get the full picture without falling back to Wails-direct calls. - WindowInfo: +4 bool fields with snake_case JSON tags. - PlatformWindow: +IsAlwaysOnTop() bool. - wailsWindow: tracks alwaysOnTop locally (Wails has no IsAlwaysOnTop accessor; mirror the SetAlwaysOnTop call). Seeded from WindowOptions.AlwaysOnTop at construction so the initial state matches what was requested. - MockWindow / mockWindow: forward IsAlwaysOnTop from the same alwaysOnTop bool the existing SetAlwaysOnTop wrote. - service.queryWindowList + queryWindowByName + taskOpenWindow snapshot: populate all four new fields. Co-Authored-By: Virgil <virgil@lethean.io>
Wails alpha.91 emits WindowHide / WindowShow / WindowMinimise / WindowUnMinimise / WindowMaximise / WindowUnMaximise / WindowFullscreen / WindowUnFullscreen / WindowRuntimeReady alongside the focus/move/ resize/closing/files-dropped events already wired. Subscribe to all nine + translate to typed core actions so consumers can react without poking application.* directly. New action types (all carry the window Name): ActionWindowHidden / ActionWindowShown ActionWindowMinimised / ActionWindowUnminimised ActionWindowMaximised / ActionWindowUnmaximised ActionWindowFullscreened / ActionWindowUnfullscreened ActionWindowRuntimeReady The WindowRuntimeReady action is the load-bearing addition for "push initial state into a window once its JS bridge is up" — the prior shape forced consumers to depend on a window.create dispatch that fires before the frontend mounts. Co-Authored-By: Virgil <virgil@lethean.io>
Mirrors the existing OnOpenedWithFile pattern. Wails fires ApplicationLaunchedWithUrl when the app is invoked via a registered URL scheme (lthn://… on macOS, ms-app://… on Windows). The wails platform adapter pulls the URL off the event context and forwards the verbatim string to the registered handler; the service wires it onto the action bus as ActionLaunchedWithUrl. Consumers route the URL via their own scheme parser; this layer stays content-neutral. Co-Authored-By: Virgil <virgil@lethean.io>
ActionFilesDropped previously forwarded only the elementID from Wails's DropTargetDetails. Now carries the full target context: id, x, y, classList, attributes — wrapped in a new DropTarget struct on ActionFilesDropped.Target (nil when the drop landed outside any data-file-drop-target region). ActionFilesDropped.TargetID is retained mirroring Target.ID for back-compat so existing consumers keep working without a nil check; new consumers should read Target for full context. PlatformWindow.OnFileDrop handler signature updated to receive *DropTarget instead of targetID string. All four implementations (wails, mock platform, internal mockWindow, mock service tests) follow. Co-Authored-By: Virgil <virgil@lethean.io>
… import
The taskEvalJS wrap is injected via pw.ExecJS as a classic script, which
has no module resolver — so its import("@wailsio/runtime") bare specifier
never resolved, the reply event never emitted, and every bridge eval timed
out. Reuse window.__lthnEmit (published by the frontend module shim) so the
already-resolved emitter carries the reply. Pairs with the desktop shim
change. TestWrapEvalScript_Contract locks the contract against regressing
to the import form.
Co-Authored-By: Virgil <virgil@lethean.io>
The internal/coreutil and internal/textutil packages carried only AX-7 label-assertion triplet stubs, leaving the real functions at 0% coverage. Add behaviour tests that exercise the production paths: - coreutil.DispatchAction — handler invocation, panic recovery, nil-Core - coreutil.ObserveResult — OK no-op, failed-Result logging, non-error Value - coreutil.LogWarn — real error, nil error, nil Core - textutil.FirstNonEmpty — first hit, untrimmed return, all-blank, no args Both packages 0.0% -> 100.0% of statements. Existing triplet stubs left intact per house convention. Co-Authored-By: Virgil <virgil@lethean.io>
The deno package's existing tests were AX-7 label-assertion + Try-wrapper smoke stubs, leaving the RPC plumbing at 0%. Add behaviour tests driving the message paths directly (no live Deno process required): - isProcessDone — terminated-error family, unrelated error, nil - marshalRPCMessage — JSON shape, round-trip, omitempty - handleMessage — result delivery to pending chan, event fan-out, unknown - handleAction — OK action result, action error, no-Core path (via io.Pipe stdin) - readLoop — newline framing + malformed-line skip + clean EOF close - send — fail-fast when not running deno 27.3% -> 57.2% of statements. Remaining uncovered paths require a live sidecar process (Start/Stop spawn, request select). Existing stubs left intact. Co-Authored-By: Virgil <virgil@lethean.io>
The pkg/mcp tool handlers (browser/clipboard/container/contextmenu/deno/
dock/notification/p2p/environment) were registered but never driven, so
each handler body sat at 0%. Add behaviour tests that call each tool via
sub.CallTool with synthetic Core actions/queries returning the typed
values the handlers expect, covering both the OK and action-error paths:
- browser_open_url, clipboard_{write,has,clear,write_image}
- container_detect_runtime, tim_{status,start,stop}
- contextmenu_{add,remove,get,list} (+ jsonBytesFromResult/resultError)
- deno_{status,start,stop,eval}, dock_{show,hide,badge,...,bounce,info}
- notification_{show,permission_request,permission_check}
- p2p_{publish,state}, theme_{get,system}
pkg/mcp 26.2% -> 35.3% of statements. Remaining uncovered handlers
(chat, dialog families, menu, tray, webview, window) follow the same
shape and can be extended likewise. Existing tests left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
…een/layout
Second pass over the pkg/mcp tool handlers, driving the dialog, tray,
menu, keybinding, screen, marketplace and layout families via CallTool
with synthetic Core actions/queries returning the typed values each
handler asserts on (string/[]string/struct payloads), plus the
action-failure path per family:
- dialog_{open_file,save_file,open_directory,confirm,info,warning,error,prompt}
- tray_{set_tooltip,set_label,info}
- menu_{get,set}, keybinding_{add,remove}
- screen_{list,get,primary,at_point,work_areas}
- marketplace_{list,fetch,verify,install}
- layout_{save,restore,delete,list}
pkg/mcp 35.3% -> 43.5% of statements. Existing tests left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
The dialog service's option-decoding helpers (typedMessageDialogOptionsFrom, hasDirectDialogOptions, info/warning/error/question/openDirectory/message DialogOptionsFrom) sat partly or wholly uncovered. Add behaviour tests exercising each resolution branch — TaskX value, typed MessageDialogOptions value, direct field options, and the failure path — asserting the correct DialogType is stamped. pkg/dialog 49.6% -> 55.7% of statements. Remaining uncovered is the Wails platform shim (wails.go) which needs a live display. Existing triplet stubs left intact. Co-Authored-By: Virgil <virgil@lethean.io>
Add behaviour tests for the pure-logic lifecycle paths:
- mode.parseAppMode — worker / manager (case-insensitive) / empty-invalid
/ unknown-defaults-to-manager; isTrue case + trim handling
- wails.mapAppEvent — EventApplicationStarted maps, Windows-only APM event
and out-of-range EventType return ok=false (routes platformMapAppEvent)
- WailsPlatform nil-app / nil-handler / nil-receiver guards — every method
is a safe no-op returning a usable cancel func
pkg/lifecycle 48.5% -> 69.1% of statements. Remaining uncovered is the
live Wails event-loop binding (real *application.App). Existing stubs
left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
The p2p Service layer (NewService, OnStartup action registration, State,
Publish/Subscribe, OptionsFromEnv, mapValue, coalesce) sat at 0% — only
the Router beneath it was tested. Add behaviour tests using the existing
fakeDriver:
- NewService / NewServiceWithDriver wire node identity into State
- Publish/Subscribe round-trip + driver-error path
- OnStartup registers p2p.publish + p2p.state actions (driven via Core),
OnShutdown no-ops a Close-less driver
- OptionsFromEnv peer-list parse + empty-env, mapValue map/struct/absent,
coalesce first-non-blank
pkg/p2p 52.3% -> 74.4% of statements. Remaining uncovered is the live TCP
driver accept/read loop. Existing stubs left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
The TCP driver's own listener path (ensureListener -> acceptLoop ->
readConn -> dispatch) was untested — existing tests only covered the
outbound Publish dial. Add real-loopback integration tests on
127.0.0.1:0:
- a subscribing driver accepts an inbound peer connection and dispatches
the framed envelope to its handler
- readConn skips a malformed line without dropping the connection; a
following valid envelope still dispatches
pkg/p2p 74.4% -> 89.2% of statements. No live display required — pure
localhost TCP.
Co-Authored-By: Virgil <virgil@lethean.io>
Cover the notification service paths the existing IPC tests missed:
- notification.send with direct field options (no task wrapper) exercising
decodeOptions / notificationOptionsFrom + ID synthesis
- registered-category action application onto an action-less notification
- notification.clear by ID through the ClearPlatform interface
- decodeOptions empty-options zero value; notificationOptionsFrom TaskSend
+ bare NotificationOptions shapes
pkg/notification 51.3% -> 58.7% of statements. Remaining uncovered is the
Wails platform shim (wails.go) which needs a live display. Existing tests
left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
…pers
The root dappco.re/go/gui package carried only AX-7 label-assertion stubs
(0% coverage). Add behaviour tests for the pure-logic surface that does
not need a live Wails event loop:
- applyModeDefaults — Tray/MultiWindow/SingleWindow/Default field fills
+ explicit-value preservation + nil-config no-op
- NewService constructs without starting; App() nil pre-startup;
OnShutdown + nil-receiver no-ops
- window-helper nil-Core / empty-name guards (Open/Hide/Exists/Adhoc/lookup)
- lookupWindow walks WindowRegistry; WindowExists/HideWindow false when
no window sub-service answers
dappco.re/go/gui 0.0% -> 13.7% of statements. Remaining uncovered is the
Wails application build path (start/buildWailsOptions/Run) which needs a
live display. Existing stubs left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
Both services route IPC options through task*FromOptions decoders + an
optsToMap helper + applyConfig; the existing tests only hit the typed-task
branch. Add behaviour tests covering all three decoder shapes (typed task,
map[string]any task, direct field options) plus the helpers:
- systray: taskSet{Tooltip,Label,Icon,TemplateIcon}FromOptions,
taskAttachWindowFromOptions, optsToMap, applyConfig (incl. "Core"
tooltip default), namedWindowHandle.Name
- menu: taskSetAppMenuFromOptions, optsToMap, applyConfig (bool + non-bool),
Manager.Build walking role/separator/submenu items (hits the mock
platform AddRole/AddSeparator/SetApplicationMenu paths)
pkg/systray 55.1% -> 63.2%, pkg/menu 57.0% -> 69.3%. Remaining uncovered
is the Wails platform shim. Existing tests left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
Drive the chat_* MCP tools via CallTool with synthetic gui.chat.* actions
returning the primitive / JSON-decoded shapes each handler expects:
- chat_send (msg id + action-error path)
- chat_history / chat_models / chat_select_model / conversations_{list,load}
through decodeChatValue
- chat_conversations_delete (success bool)
- chat_thinking_{start,stop} (ChatThinkingState)
- decodeChatValue direct round-trip
pkg/mcp 43.5% -> 46.5% of statements. Existing tests left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
Drive the 32 window_* MCP tools via CallTool with synthetic window.*
actions + window list/by-name queries:
- action-only verbs (close/maximize/minimize/restore/focus/toggle_*/zoom_*/
flash/print) report success
- parameterised verbs (position/size/always_on_top/fullscreen/zoom_set/
url_set/html_set/exec_js/content_protection/background_colour) accept
their geometry/state args
- window_list reads the WindowInfo list query
- window_close action-error path
pkg/mcp 46.5% -> 50.5% of statements. Existing tests left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
Drive the webview_* MCP tools via CallTool with synthetic webview.*
actions + URL/title/DOM/console queries:
- webview_eval returns the eval result
- action verbs (click/type/navigate/scroll/hover/select/check/upload/
viewport/clear_console) report success
- string queries (url/title/dom_tree/source) + console decode their values
- webview_navigate action-error path
pkg/mcp 50.5% -> 54.0% of statements. Remaining uncovered webview handlers
(screenshot/element-info/network/performance/pdf) carry richer typed
results. Existing tests left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
…pboard tools Close the scattered 0% MCP tool handlers: - dock_info (visibility query) + dock_set_progress_bar - theme_set (environment.set_theme -> ThemeInfo) - event_emit (cancelled bool + action-error) + event_list (ListenerInfo) - clipboard_read_image (ImageContent -> base64) pkg/mcp 54.0% -> 55.7% of statements. Existing tests left intact. Co-Authored-By: Virgil <virgil@lethean.io>
- environment.normalizeTheme (system/dark/light + invalid-reject), themeName,
validatedOpenFileManagerPath (empty / null-byte / relative / clean-abs)
- contextmenu.invalidTaskResult scoped-error Result
pkg/environment 56.7% -> 59.2%, pkg/contextmenu 58.0% -> 58.5%. Existing
tests left intact.
Co-Authored-By: Virgil <virgil@lethean.io>
Advance external/go workspace submodule to v0.10.4 so dev (GOWORK on) and standalone (GOWORK=off) builds resolve the same core/go. Core-only change; an unrelated in-flight config sibling bump in the working tree is left unstaged. Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Virgil <virgil@lethean.io>
|
Important Review skippedToo many files! This PR contains 2259 files, which is 2109 over the limit of 150. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (741)
📒 Files selected for processing (2259)
You can disable this status message by setting the Use the checkbox below for a quick retry:
Warning Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption. |
|



This pull request introduces two main changes: it adds formal licensing and documentation, and updates several external dependencies and their paths. The most significant update is the addition of the EUPL-1.2 license, which clarifies the legal terms for using and distributing the code. Additionally, a new documentation file explains the "event re-broadcast" pattern for frontend-backend communication. Several external submodules are updated to newer versions, and their paths are adjusted for better Go module compatibility.
Licensing and Documentation
LICENCEfile with the European Union Public Licence v. 1.2 (EUPL-1.2), specifying the project's open-source terms and obligations.docs/event-rebroadcast.md, documenting the event re-broadcast pattern used for bridging native GUI events to frontend custom events, including usage examples and guidance for app developers.External Dependency Updates
external/config,external/go,external/io,external/log, andexternal/webviewto newer commit versions, ensuring the project uses the latest upstream changes. [1] [2] [3] [4] [5]go.workto reference the/gosubdirectories of external dependencies, improving compatibility with Go's module system.