Skip to content

Commit 59089db

Browse files
hyperpolymathclaude
andcommitted
feat: Gossamer migration — RuntimeBridge, gossamer.conf.json, Tauri→Gossamer conversion
Added Gossamer configuration and RuntimeBridge alongside existing Tauri setup. Tauri files preserved for transition period. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8466065 commit 59089db

32 files changed

Lines changed: 9757 additions & 5244 deletions

lithoglyph/studio/deno.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
"tasks": {
66
"dev": "cargo tauri dev",
77
"build": "cargo tauri build",
8+
"gossamer:dev": "gossamer dev",
9+
"gossamer:build": "gossamer build",
810
"rescript": "^12.0.0",
911
"rescript:build": "npx rescript build",
10-
"bundle": "deno run -A npm:esbuild src/main.tsx --bundle --outfile=dist/app.js --format=esm --external:@tauri-apps/api",
12+
"bundle": "deno run -A npm:esbuild src/main.tsx --bundle --outfile=dist/app.js --format=esm --external:@tauri-apps/api --external:@gossamer/api",
1113
"fmt": "deno fmt src/"
1214
},
1315
"imports": {
1416
"@tauri-apps/api": "npm:@tauri-apps/api@^2",
17+
"@gossamer/api": "npm:@gossamer/api@^0.1",
1518
"react": "npm:react@^18",
1619
"react-dom": "npm:react-dom@^18",
1720
"rescript": "^12.0.0",
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{
2+
"$schema": "https://gossamer.dev/schemas/config/v1",
3+
"productName": "Lith Studio",
4+
"version": "0.1.0",
5+
"identifier": "com.hyperpolymath.lithoglyph-studio",
6+
"build": {
7+
"frontendDist": "./dist",
8+
"devUrl": "http://localhost:8000/",
9+
"beforeDevCommand": "deno task dev",
10+
"beforeBuildCommand": "deno task build"
11+
},
12+
"app": {
13+
"windows": [
14+
{
15+
"label": "main",
16+
"title": "Lith Studio",
17+
"width": 1200,
18+
"height": 800,
19+
"minWidth": null,
20+
"minHeight": null,
21+
"maxWidth": null,
22+
"maxHeight": null,
23+
"resizable": true,
24+
"fullscreen": false,
25+
"decorations": true,
26+
"transparent": false,
27+
"center": true,
28+
"alwaysOnTop": false,
29+
"visible": true,
30+
"url": "/"
31+
}
32+
],
33+
"security": {
34+
"csp": null,
35+
"capabilities": [
36+
"filesystem",
37+
"network",
38+
"shell",
39+
"clipboard",
40+
"notification"
41+
],
42+
"capabilityTokens": {
43+
"enabled": false,
44+
"issuer": "gossamer-runtime",
45+
"ttl": 3600
46+
},
47+
"sandbox": {
48+
"enabled": true,
49+
"allowExec": false,
50+
"allowNetwork": true,
51+
"allowFilesystem": true
52+
}
53+
},
54+
"ipc": {
55+
"protocol": "json",
56+
"bridgeInjection": true,
57+
"maxMessageSize": 16777216,
58+
"timeout": 30000
59+
},
60+
"tray": {
61+
"enabled": false,
62+
"icon": null,
63+
"tooltip": null,
64+
"menuOnLeftClick": true
65+
}
66+
},
67+
"plugins": {},
68+
"bundle": {
69+
"active": true,
70+
"targets": [
71+
"deb",
72+
"appimage",
73+
"dmg",
74+
"nsis"
75+
],
76+
"icon": [
77+
"icons/32x32.png",
78+
"icons/128x128.png",
79+
"icons/128x128@2x.png",
80+
"icons/icon.icns",
81+
"icons/icon.ico"
82+
],
83+
"resources": [],
84+
"copyright": "Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath)",
85+
"license": "PMPL-1.0-or-later",
86+
"shortDescription": "Lith Studio - Zero-friction interface for dependently-typed databases",
87+
"longDescription": "",
88+
"linux": {
89+
"desktopEntry": true,
90+
"category": "Development",
91+
"section": "utils",
92+
"depends": [],
93+
"appimage": {
94+
"bundleMediaFramework": false
95+
}
96+
},
97+
"macos": {
98+
"bundleIdentifier": "com.hyperpolymath.lithoglyph-studio",
99+
"minimumSystemVersion": "11.0",
100+
"frameworks": [],
101+
"entitlements": null,
102+
"signingIdentity": null,
103+
"notarization": {
104+
"enabled": false,
105+
"teamId": null
106+
}
107+
},
108+
"windows": {
109+
"webview2": "embed",
110+
"certificateThumbprint": null,
111+
"wix": {
112+
"language": "en-US"
113+
},
114+
"nsis": {
115+
"displayLanguageSelector": false,
116+
"installerIcon": null
117+
}
118+
}
119+
},
120+
"ephapax": {
121+
"modules": [],
122+
"region": "app",
123+
"linearVerification": true,
124+
"regionBoundary": "strict",
125+
"preload": []
126+
}
127+
}

lithoglyph/studio/src/App.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ module Tauri = Types.Tauri
99
module ServiceStatus = Types.ServiceStatus
1010
module AppInfo = Types.AppInfo
1111

12-
// Validation result from Rust backend
12+
// Validation result from backend
1313
type validationResult = {
1414
valid: bool,
1515
errors: array<string>,
1616
proofs_generated: array<string>,
1717
}
1818

19-
// Check service status from Tauri backend
19+
// Check service status from backend
2020
let checkServiceStatus = async (): option<ServiceStatus.t> => {
2121
try {
2222
let result = await Tauri.invoke("check_service_status", ())

lithoglyph/studio/src/DataEntryPanel.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ let make = (~collections: array<Collection.t>) => {
279279
ReactEvent.Form.preventDefault(evt)
280280
if allFieldsValid && provenance.source != "" && provenance.rationale != "" {
281281
setSubmission(_ => Submitting)
282-
// TODO: Call Tauri command to insert document
282+
// TODO: Call backend command to insert document
283283
let _ = setTimeout(() => {
284284
setSubmission(_ => Success("Document inserted successfully with provenance tracking"))
285285
setDocument(_ => Document.empty())

lithoglyph/studio/src/NormalizationPanel.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ let make = (~collections: array<Collection.t>) => {
304304

305305
let handleDiscoverFDs = () => {
306306
setDiscoveryState(_ => Discovering)
307-
// TODO: Call Tauri command to discover FDs
307+
// TODO: Call backend command to discover FDs
308308
let _ = setTimeout(() => {
309309
setDiscoveryState(_ =>
310310
Discovered([
@@ -332,7 +332,7 @@ let make = (~collections: array<Collection.t>) => {
332332
}
333333

334334
let handleApplyProposal = () => {
335-
// TODO: Call Tauri command to apply normalization
335+
// TODO: Call backend command to apply normalization
336336
Console.log("Applying normalization proposal")
337337
}
338338

lithoglyph/studio/src/ProofAssistant.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ let make = () => {
277277

278278
let handleApplyFix = (code: string) => {
279279
Console.log2("Applying fix:", code)
280-
// TODO: Call Tauri command to apply fix
280+
// TODO: Call backend command to apply fix
281281
}
282282

283283
<section className="proof-assistant">

lithoglyph/studio/src/QueryBuilder.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ let make = (~collections: array<Collection.t>) => {
324324

325325
let handleRunQuery = _ => {
326326
setResults(_ => QueryResults.Loading)
327-
// TODO: Call Tauri command to execute query
327+
// TODO: Call backend command to execute query
328328
// For now, simulate with placeholder
329329
let _ = setTimeout(() => {
330330
setResults(_ => QueryResults.Success([]))
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath)
3+
4+
/// RuntimeBridge — Unified IPC bridge for Lith Studio.
5+
///
6+
/// Detects the available runtime (Gossamer, Tauri, or browser-only) and
7+
/// dispatches `invoke` calls to the appropriate backend. This allows all
8+
/// command modules to use a single import instead of binding directly
9+
/// to `@tauri-apps/api/core`.
10+
///
11+
/// Priority order:
12+
/// 1. Gossamer (`window.__gossamer_invoke`) — own stack, preferred
13+
/// 2. Tauri (`window.__TAURI_INTERNALS__`) — legacy, transition
14+
/// 3. Browser (direct HTTP fetch) — development fallback
15+
///
16+
/// Migration path: replace
17+
/// `Tauri.invoke("cmd", args)`
18+
/// with
19+
/// `RuntimeBridge.invoke("cmd", args)`
20+
21+
// ---------------------------------------------------------------------------
22+
// Raw external bindings — exactly one of these will be available at runtime
23+
// ---------------------------------------------------------------------------
24+
25+
/// Gossamer IPC: injected by gossamer_channel_open() into the webview.
26+
/// Signature: (commandName: string, payload: object) => Promise<any>
27+
%%raw(`
28+
function isGossamerRuntime() {
29+
return typeof window !== 'undefined'
30+
&& typeof window.__gossamer_invoke === 'function';
31+
}
32+
`)
33+
@val external isGossamerRuntime: unit => bool = "isGossamerRuntime"
34+
35+
%%raw(`
36+
function gossamerInvoke(cmd, args) {
37+
return window.__gossamer_invoke(cmd, args);
38+
}
39+
`)
40+
@val external gossamerInvoke: (string, 'a) => promise<'b> = "gossamerInvoke"
41+
42+
/// Tauri IPC: injected by the Tauri runtime into the webview.
43+
%%raw(`
44+
function isTauriRuntime() {
45+
return typeof window !== 'undefined'
46+
&& window.__TAURI_INTERNALS__ != null
47+
&& !window.__TAURI_INTERNALS__.__BROWSER_SHIM__;
48+
}
49+
`)
50+
@val external isTauriRuntime: unit => bool = "isTauriRuntime"
51+
52+
@module("@tauri-apps/api/core")
53+
external tauriInvoke: (string, 'a) => promise<'b> = "invoke"
54+
55+
// ---------------------------------------------------------------------------
56+
// Unified invoke — detects runtime and dispatches
57+
// ---------------------------------------------------------------------------
58+
59+
/// The runtime currently in use. Cached after first detection for performance.
60+
type runtime =
61+
| Gossamer
62+
| Tauri
63+
| BrowserOnly
64+
65+
%%raw(`
66+
var _detectedRuntime = null;
67+
function detectRuntime() {
68+
if (_detectedRuntime !== null) return _detectedRuntime;
69+
if (typeof window !== 'undefined' && typeof window.__gossamer_invoke === 'function') {
70+
_detectedRuntime = 'gossamer';
71+
} else if (typeof window !== 'undefined' && window.__TAURI_INTERNALS__ != null && !window.__TAURI_INTERNALS__.__BROWSER_SHIM__) {
72+
_detectedRuntime = 'tauri';
73+
} else {
74+
_detectedRuntime = 'browser';
75+
}
76+
return _detectedRuntime;
77+
}
78+
`)
79+
@val external detectRuntimeRaw: unit => string = "detectRuntime"
80+
81+
/// Detect and return the current runtime.
82+
let detectRuntime = (): runtime => {
83+
switch detectRuntimeRaw() {
84+
| "gossamer" => Gossamer
85+
| "tauri" => Tauri
86+
| _ => BrowserOnly
87+
}
88+
}
89+
90+
/// Invoke a backend command through whatever runtime is available.
91+
///
92+
/// - On Gossamer: calls `window.__gossamer_invoke(cmd, args)`
93+
/// - On Tauri: calls `window.__TAURI_INTERNALS__.invoke(cmd, args)`
94+
/// - On browser: rejects with a descriptive error
95+
///
96+
/// This is the primary function all command modules should use.
97+
let invoke = (cmd: string, args: 'a): promise<'b> => {
98+
if isGossamerRuntime() {
99+
gossamerInvoke(cmd, args)
100+
} else if isTauriRuntime() {
101+
tauriInvoke(cmd, args)
102+
} else {
103+
Promise.reject(
104+
JsError.throwWithMessage(
105+
`No desktop runtime — "${cmd}" requires Gossamer or Tauri`,
106+
),
107+
)
108+
}
109+
}
110+
111+
/// Check whether any desktop runtime is available.
112+
let hasDesktopRuntime = (): bool => {
113+
isGossamerRuntime() || isTauriRuntime()
114+
}
115+
116+
/// Get a human-readable name for the current runtime.
117+
let runtimeName = (): string => {
118+
switch detectRuntime() {
119+
| Gossamer => "Gossamer"
120+
| Tauri => "Tauri"
121+
| BrowserOnly => "Browser"
122+
}
123+
}

lithoglyph/studio/src/Types.res

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ module Collection = {
3636
}
3737
}
3838

39-
// Tauri command bindings
39+
// Backend command bindings — delegates to RuntimeBridge for Gossamer/Tauri detection
4040
module Tauri = {
4141
type invokeResult<'a>
4242

43-
@module("@tauri-apps/api/core")
44-
external invoke: (string, 'a) => promise<'b> = "invoke"
43+
/// Use RuntimeBridge.invoke for runtime-agnostic backend calls.
44+
/// Kept as `Tauri` module name for backward compatibility with existing call sites.
45+
let invoke = RuntimeBridge.invoke
4546
}
4647

4748
// Validation state for GQLdt preview

verisimdb/admin/deno.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@hyperpolymath/verisimdb-admin",
3+
"version": "0.1.0",
4+
"license": "PMPL-1.0-or-later",
5+
"tasks": {
6+
"gossamer:dev": "deno task res:build && deno task css:build && deno run --allow-net --allow-read --allow-env dev-server.ts",
7+
"gossamer:build": "deno task res:build && deno task css:build && deno run --allow-all build.ts",
8+
"res:build": "npx rescript build",
9+
"res:watch": "npx rescript build -w",
10+
"bundle": "deno task gossamer:build && gossamer bundle",
11+
"css:build": "cp src/styles.css public/styles.css",
12+
"check": "deno check dev-server.ts build.ts",
13+
"clean": "npx rescript clean && rm -rf public/*.js public/*.css"
14+
},
15+
"imports": {
16+
"@gossamer/api": "https://deno.land/x/gossamer_api@0.1.0/mod.ts",
17+
"rescript": "npm:rescript@12",
18+
"@rescript/core": "npm:@rescript/core@1"
19+
},
20+
"compilerOptions": {
21+
"strict": true,
22+
"noUnusedLocals": true
23+
}
24+
}

0 commit comments

Comments
 (0)