Public, free to read, reuse, and adapt — Kirk Patrick Miller's notice for this edition. Not a patent grant.
FLINT_HEAL_CONNECT_v0.4 (build only) · c04
cf5f565f85 · words 1408 · characters 35603–48442
Previous section · Next section
Exact section text · Full source · Source index
- Training evidence — explicit wording in the guide row
## 9. Alpha: one restore path when leaving Settings (Hypha item 9)
**Cause:** `openPlace` hides galaxy nodes for Settings and marks them `data-flc-was-hidden` (`garden-rooms.js:1649-1661`), but only `closePlace` puts them back. Settings then Nursery keeps both hide classes. Settings twice marks the already hidden nodes as `'1'`, so close never unhides them.
**Fix:** a small `flcRestoreGalaxy()` inside `bindPlaceDoors`: unmount FlConnect, hide the mount, drop both classes, unhide only `'0'` nodes, and remove every mark. Call it first in `openPlace` and in `closePlace`. The v0.3 restore code stays (it becomes a no-op).
```diff
--- a/docs/modules/garden-rooms.js
+++ b/docs/modules/garden-rooms.js
@@ -1585,8 +1585,27 @@
var closeBtn = document.getElementById('place-veil-close');
var doors = document.querySelectorAll('[data-garden-place]');
+ // v-connect-heal-v0.4 / Hypha walk 3 item 9: one restore path for leaving
+ // Settings (close, Nursery, Core, or Settings again). Put back only what
+ // Settings hid, then forget the marks so a second open records fresh.
+ function flcRestoreGalaxy() {
+ try {
+ if (window.FlConnect && typeof FlConnect.unmount === 'function') FlConnect.unmount();
+ var host = document.getElementById('fl-connect-mount-garden');
+ if (host) host.hidden = true;
+ document.documentElement.classList.remove('flc-hide-galaxy');
+ document.body.classList.remove('fl-connect-open');
+ var marked = document.querySelectorAll('[data-flc-was-hidden]');
+ for (var mi = 0; mi < marked.length; mi++) {
+ if (marked[mi].getAttribute('data-flc-was-hidden') === '0') marked[mi].hidden = false;
+ marked[mi].removeAttribute('data-flc-was-hidden');
+ }
+ } catch (eR) {}
+ }
+
function closePlace(opts) {
if (!veil) return;
+ flcRestoreGalaxy();
// v-connect-under-more-v0 heal v0.1 — stop FlConnect loop when Settings room closes
try {
if (window.FlConnect && typeof FlConnect.unmount === 'function') FlConnect.unmount();
@@ -1617,6 +1636,7 @@
function openPlace(id) {
if (!veil) return;
+ flcRestoreGalaxy();
hideLuminoMenu();
if (window.GardenRooms && GardenRooms.closeThread) {
GardenRooms.closeThread({ silentLabel: true });
@@ -1648,6 +1668,7 @@
}
} else if (id === 'settings') {
veil.classList.add('is-settings');
+ try { veil.scrollTop = 0; } catch (eTop) {}
// v-connect-port-picker-v0 / Hypha H5 — hide orb/planets while Connect/Settings open
try {
document.documentElement.classList.add('flc-hide-galaxy');
```
Measured (galaxy nav, galaxy word, luminos):
| Sequence | Main | After |
|---|---|---|
| Settings then Nursery | nav and word hidden in Nursery, `fl-connect-open` stays | same as a fresh Nursery |
| Settings, Settings, close | `.galaxy-nav` and `#galaxy-word` stuck hidden | all back |
---
## 10. Shared `fl-connect.js`: remember() keeps isLocal (Hypha item 10)
**Cause:** `remember()` sets `state.isLocal = true`, then calls `handleLocalToggle(true)` (`fl-connect.js:345`), which reads the unchecked `#localToggle` and writes `state.isLocal = false` (`app.html:35423-35426`).
**Fix:** check the toggle first (the same thing `connectOllama` already does at `27730-27731`). This hunk also hides the Local AI toast after a mind is chosen (item 7).
```diff
--- a/docs/modules/fl-connect.js
+++ b/docs/modules/fl-connect.js
@@ -342,9 +354,13 @@
root.state.provider = 'ollama';
if (name) root.state.ollamaModel = name;
}
+ // v-connect-heal-v0.4 item 10: handleLocalToggle reads #localToggle, so check it first
+ try { var lt = document.getElementById('localToggle'); if (lt) lt.checked = true; } catch (eLt) {}
if (typeof root.handleLocalToggle === 'function') root.handleLocalToggle(true);
if (typeof root.updateStatus === 'function') root.updateStatus();
if (root.AiSetup && typeof root.AiSetup.updateStatus === 'function') root.AiSetup.updateStatus();
+ // v-connect-heal-v0.4 item 7: once they chose a mind, the Local AI toast stops asking
+ try { var tt = document.getElementById('flOllamaToast'); if (tt) tt.classList.remove('fl-toast-show'); } catch (eTt) {}
} catch (eF) {}
stopLoop();
return { name: name, base: base };
```
Markers and one readability line (the "All Bridge downloads" link was dark blue on night, now gold like Other ways):
```diff
--- a/docs/modules/fl-connect.js
+++ b/docs/modules/fl-connect.js
@@ -1,7 +1,7 @@
// ═══════════════════════════════════════════════════════════════
// fl-connect.js — FlConnect shared core (FreeLattice + theLatticeTree)
//
-// Marker: v-connect-under-more-v0 · heal v0.1 · v-connect-port-picker-v0 · heal v0.2
+// Marker: v-connect-under-more-v0 · heal v0.1 · v-connect-port-picker-v0 · heal v0.2 · v-connect-heal-v0.4
// Layer, never delete. Prefer helped Bridge (11435…) before bare 11434.
// Port-only loopback: fl_localPort_manual holds "11500" only — never foreign hosts.
// Soft leave sw.js. Alpha: fl_alpha_local_mind. Never bare *.
@@ -622,6 +650,7 @@
'.flc-btn{display:inline-block;min-height:48px;line-height:48px;padding:0 1.1rem;border-radius:10px;text-decoration:none;font-weight:600;border:0;cursor:pointer;font:inherit}',
'.flc-primary{background:rgba(52,211,153,.18);border:1px solid rgba(52,211,153,.45);color:#34d399}',
'.flc-soft{font-size:.8rem;color:rgba(148,163,184,.95);margin:.65rem 0 0;line-height:1.45}',
+ '.flc-soft a{color:#e8b019}',
'.flc-other,.flc-builders{margin:1rem 0;font-size:.88rem;color:rgba(200,210,230,.8)}',
'.flc-other ul{margin:.5rem 0 0;padding-left:1.2rem}',
'.flc-other a{color:#e8b019}',
```
After this, copy `docs/modules/fl-connect.js` to Alpha byte for byte.
---
## 11. FreeLattice smoke: `docs/scripts/smoke-connect-heal-v0.4.js` (new)
Static checks plus runtime checks in `vm` (Settings address save, un-helped manual Bridge, refusal, Use automatic, remember). Locks: index equals app, sw.js equals main, no `confirm(` and no em dash in added lines, no conflict markers, inline scripts parse. It fails on main at the first marker check, and fails if the item 4 `return report;` is removed.
```js
#!/usr/bin/env node
// v-connect-heal-v0.4: Hypha walk 3 heals (FreeLattice side).
// Quiet Bridge interval, cold #connect lands once, manual port asks for
// Yes, help, Use automatic really clears, loopback-only Ollama Address,
// no silent connect or model pick, modal self-heal, remember keeps isLocal.
'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const { execSync } = require('child_process');
const root = path.join(__dirname, '..');
const repo = path.join(root, '..');
const read = (r) => fs.readFileSync(path.join(root, r), 'utf8');
const app = read('app.html');
const mod = read('modules/fl-connect.js');
const indexHtml = fs.readFileSync(path.join(repo, 'index.html'), 'utf8');
const rootSw = fs.readFileSync(path.join(repo, 'sw.js'), 'utf8');
function fnBody(src, sig) {
const i = src.indexOf(sig);
assert.ok(i >= 0, 'missing ' + sig);
const end = src.indexOf('\n}\n', i);
return src.slice(i, end + 2);
}
// Markers
assert.ok(/v-connect-heal-v0\.4/.test(mod), 'marker in fl-connect.js');
assert.ok(/v-connect-heal-v0\.4/.test(app), 'marker in app.html');
// Item 2: Bridge interval is quiet
const boot = app.slice(app.indexOf('(function flBridgeDetectBoot()'), app.indexOf('window.flBridgeDetect = flBridgeDetect;'));
assert.ok(!/setInterval\(run,\s*20000\)/.test(boot), 'no forever 11-port scan');
assert.ok(/flBridgeQuietTick/.test(boot) && /document\.hidden/.test(boot) && /flBridgeHealthAt\(saved\)/.test(boot), 'quiet tick: visible, saved port only');
// Item 3: cold #connect
const go = app.slice(app.indexOf('var _flcUserMoved = false;'), app.indexOf("window.addEventListener('hashchange', goConnect)"));
assert.ok(/_flcUserMoved/.test(go) && /pointerdown/.test(app) && /history\.replaceState/.test(go), 'first tap ends retries and clears #connect');
assert.ok(/tab-connect[\s\S]{0,120}classList\.contains\('active'\)\) return;/.test(go), 'no remount when Connect already showing');
// Item 5: manual port wins in getOllamaBaseUrl
const gob = fnBody(app, 'function getOllamaBaseUrl() {');
assert.ok(gob.indexOf('flManualBaseFirst()') > 0 && gob.indexOf('flManualBaseFirst()') < gob.indexOf("fl_ollamaHost"), 'manual before fl_ollamaHost');
// Item 6: loopback-only Settings address, runtime check
assert.ok(!/Change to a remote IP/.test(app), 'no remote IP invite');
assert.ok(/id="ollamaHostRefusal"/.test(app), 'refusal line present');
{
const store = { fl_ollamaHost: '127.0.0.1:11434' };
const els = {
ollamaHostInput: { value: '' },
ollamaHostRefusal: { hidden: true, textContent: '' }
};
const ctx = {
localStorage: {
getItem: (k) => (k in store ? store[k] : null),
setItem: (k, v) => { store[k] = String(v); },
removeItem: (k) => { delete store[k]; }
},
document: { getElementById: (id) => els[id] || null },
URL,
_ollamaResolvedBase: { base: 'x' },
updateOllamaProviderUrl() {},
FLBoxPointer: undefined
};
vm.runInNewContext(fnBody(app, 'function flWriteOllamaHostLoopbackOnly(host) {') + fnBody(app, 'function flSaveOllamaHost() {') + '\nthis.save = flSaveOllamaHost;', ctx);
els.ollamaHostInput.value = '192.168.1.50:11434';
ctx.save();
assert.strictEqual(store.fl_ollamaHost, '127.0.0.1:11434', 'LAN refused, saved address unchanged');
assert.strictEqual(els.ollamaHostRefusal.hidden, false, 'friendly refusal shown');
assert.ok(/only talks to your own computer/.test(els.ollamaHostRefusal.textContent), 'refusal words');
els.ollamaHostInput.value = 'localhost:11500';
ctx.save();
assert.strictEqual(store.fl_ollamaHost, '127.0.0.1:11500', 'loopback port saved as 127.0.0.1');
assert.strictEqual(els.ollamaHostRefusal.hidden, true, 'refusal clears');
}
// Item 7: no silent connect or model pick
const co = app.slice(app.indexOf('function connectOllama(models) {'), app.indexOf('// Main app load detection'));
assert.ok(co.indexOf('FlConnect.open()') > 0 && co.indexOf('FlConnect.open()') < co.indexOf('models[0].name'), 'connectOllama opens Connect before any models[0]');
assert.ok(!/Connected to a mind on this computer!/.test(app), 'toast no longer claims connected');
const ac = app.slice(app.indexOf('function flAutoConnect(opts) {'), app.indexOf('var targets = ['));
assert.ok(/FlConnect\.open/.test(ac) && /return;\s*\}/.test(ac), 'Zero-Click returns before probing when FlConnect exists');
const ad = app.slice(app.indexOf('async function autoDiscoverAI() {'), app.indexOf('// Auto-connect to the best ready server'));
assert.ok(/if \(window\.FlConnect && typeof FlConnect\.open === 'function'\) return false;/.test(ad), 'autoDiscoverAI finds but never connects by itself');
assert.ok(/flOllamaToast'\); if \(tt\) tt\.classList\.remove\('fl-toast-show'\)/.test(mod), 'toast stops asking after a mind is chosen');
// Item 8: Settings Local opens Connect; modal self-heals
const ssm = app.slice(app.indexOf("if (mode === 'local') {"), app.indexOf("flOpenConnectWizard('local')"));
assert.ok(/FlConnect\.open\(\)[\s\S]{0,40}return;/.test(ssm), 'Local mode opens Connect first');
assert.ok(/modalOverlayEl && !document\.body\.contains\(modalOverlayEl\)\) modalOverlayEl = null;/.test(app), 'openModal forgets detached overlay');
assert.ok(/data-flc-cloud-key/.test(mod) && /AiSetup\.openModal\(\)/.test(mod), 'Connect has a real cloud key door');
// fl-connect.js runtime: items 4, 5, 10
function sandbox(store, extra) {
const sb = Object.assign({
localStorage: {
getItem: (k) => (Object.prototype.hasOwnProperty.call(store, k) ? store[k] : null),
setItem: (k, v) => { store[k] = String(v); },
removeItem: (k) => { delete store[k]; }
},
location: { hostname: 'freelattice.com', protocol: 'https:', hash: '', search: '' },
document: {
hidden: false,
documentElement: { getAttribute: () => '', classList: { add() {}, remove() {}, contains() { return false; } } },
getElementById: () => null,
head: { appendChild() {} },
body: { contains: () => false, classList: { add() {}, remove() {} } },
addEventListener() {}, removeEventListener() {}, querySelectorAll: () => []
},
fetch: async () => { throw new Error('offline'); },
setTimeout: () => 1, clearTimeout() {},
navigator: { userAgent: 't' },
console, JSON, String, parseInt, URL, Date, Math,
AbortSignal: { timeout: () => undefined }
}, extra || {});
sb.window = sb;
vm.runInNewContext(mod, sb);
return sb;
}