@andespindola/brainlink 0.1.0-beta.36 → 0.1.0-beta.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
- Added cross-platform native desktop GUI auto-open for `blink server` (macOS Swift/WebKit, Windows PowerShell WinForms, Linux Python GTK/WebKit2), with app-window/browser fallback.
|
|
29
29
|
- Changed Linux default UI launch to app-window/browser for lighter startup; Linux native GUI is now opt-in via `BRAINLINK_LINUX_NATIVE_GUI=1`.
|
|
30
30
|
- Added native GUI parent-process monitoring so GUI windows close automatically when `blink server` stops.
|
|
31
|
+
- Improved non-mac browser detection fallback to try installed Edge/Chrome/Firefox/Chromium candidates before system default open.
|
|
32
|
+
- Improved graph filter rendering to keep hub anchor nodes visible (`Memory Hub`/`MOC`/high-degree fallback) for coherent relationship context.
|
|
31
33
|
|
|
32
34
|
## 0.1.0-beta.3
|
|
33
35
|
|
package/COPYRIGHT.md
CHANGED
package/README.md
CHANGED
|
@@ -571,6 +571,7 @@ The graph UI shows:
|
|
|
571
571
|
- neutral graph nodes with segment/group metadata
|
|
572
572
|
- agent selector (id-only labels) for isolated views
|
|
573
573
|
- graph filter matches title, path, tags and note content
|
|
574
|
+
- graph filter keeps hub context nodes visible (`Memory Hub`/`MOC`/high-degree fallback) to preserve relationship readability
|
|
574
575
|
- realtime refresh while `--watch` is enabled
|
|
575
576
|
- graph controls for zoom in, zoom out, fit visible nodes and reset-to-fit-all
|
|
576
577
|
- wheel zoom (including `cmd+scroll` and `ctrl+scroll`) anchored to cursor position for faster navigation in large graphs
|
|
@@ -1055,7 +1056,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
1055
1056
|
## License
|
|
1056
1057
|
|
|
1057
1058
|
MIT. See [LICENSE](LICENSE).
|
|
1058
|
-
Copyright (c) 2026
|
|
1059
|
+
Copyright (c) 2026 Substructa. See [COPYRIGHT.md](COPYRIGHT.md).
|
|
1059
1060
|
|
|
1060
1061
|
### Memory Optimization Loop (1-7)
|
|
1061
1062
|
|
|
@@ -46,8 +46,8 @@ export const createClientHtml = () => `<!doctype html>
|
|
|
46
46
|
<canvas id="graph" aria-label="Brainlink knowledge graph"></canvas>
|
|
47
47
|
</section>
|
|
48
48
|
</main>
|
|
49
|
-
<footer class="app-footer" aria-label="
|
|
50
|
-
<small>
|
|
49
|
+
<footer class="app-footer" aria-label="Copyright notice">
|
|
50
|
+
<small>Copyright © 2026 Substructa</small>
|
|
51
51
|
</footer>
|
|
52
52
|
<dialog id="contentDialog" class="content-dialog" aria-labelledby="contentTitle">
|
|
53
53
|
<article>
|
|
@@ -99,6 +99,8 @@ const resize = () => {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const normalizeQuery = value => value.trim().toLowerCase()
|
|
102
|
+
const hubNodeRetentionLimit = 2
|
|
103
|
+
const hubNodePattern = /\b(memory\s*hub|knowledge\s*hub|hub|moc|map|memory\s*map|mapa)\b/i
|
|
102
104
|
|
|
103
105
|
const localFilteredNodes = query =>
|
|
104
106
|
state.nodes.filter(node =>
|
|
@@ -107,14 +109,51 @@ const localFilteredNodes = query =>
|
|
|
107
109
|
node.tags.some(tag => tag.toLowerCase().includes(query))
|
|
108
110
|
)
|
|
109
111
|
|
|
112
|
+
const rankedHubNodes = () => {
|
|
113
|
+
if (state.nodes.length === 0) {
|
|
114
|
+
return []
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const byTitleAndDegree = [...state.nodes]
|
|
118
|
+
.filter(node => hubNodePattern.test(node.title) || hubNodePattern.test(node.path) || node.tags.some(tag => hubNodePattern.test(tag)))
|
|
119
|
+
.sort((left, right) => {
|
|
120
|
+
const byDegree = (state.nodeDegrees.get(right.id) ?? 0) - (state.nodeDegrees.get(left.id) ?? 0)
|
|
121
|
+
if (byDegree !== 0) return byDegree
|
|
122
|
+
return left.title.localeCompare(right.title)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (byTitleAndDegree.length > 0) {
|
|
126
|
+
return byTitleAndDegree.slice(0, hubNodeRetentionLimit)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return [...state.nodes]
|
|
130
|
+
.sort((left, right) => {
|
|
131
|
+
const byDegree = (state.nodeDegrees.get(right.id) ?? 0) - (state.nodeDegrees.get(left.id) ?? 0)
|
|
132
|
+
if (byDegree !== 0) return byDegree
|
|
133
|
+
return left.title.localeCompare(right.title)
|
|
134
|
+
})
|
|
135
|
+
.slice(0, 1)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const withPersistentHubNodes = nodes => {
|
|
139
|
+
if (nodes.length === 0) {
|
|
140
|
+
return rankedHubNodes()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const ids = new Set(nodes.map(node => node.id))
|
|
144
|
+
const hubsToKeep = rankedHubNodes().filter(node => !ids.has(node.id))
|
|
145
|
+
return nodes.concat(hubsToKeep)
|
|
146
|
+
}
|
|
147
|
+
|
|
110
148
|
const filteredNodes = () => {
|
|
111
149
|
const query = normalizeQuery(state.query)
|
|
112
150
|
if (!query) return state.nodes
|
|
113
151
|
if (state.contentFilter.query === query && state.contentFilter.ids instanceof Set) {
|
|
114
|
-
|
|
152
|
+
const matched = state.nodes.filter(node => state.contentFilter.ids.has(node.id))
|
|
153
|
+
return withPersistentHubNodes(matched)
|
|
115
154
|
}
|
|
116
155
|
|
|
117
|
-
return localFilteredNodes(query)
|
|
156
|
+
return withPersistentHubNodes(localFilteredNodes(query))
|
|
118
157
|
}
|
|
119
158
|
|
|
120
159
|
const recomputeVisibility = () => {
|
|
@@ -207,6 +207,10 @@ const commandExists = (command) => {
|
|
|
207
207
|
}
|
|
208
208
|
};
|
|
209
209
|
const envFlagEnabled = (name) => process.env[name] === '1' || process.env[name] === 'true';
|
|
210
|
+
const spawnAnyDetached = (candidates) => candidates.some(([command, args]) => spawnDetached(command, args));
|
|
211
|
+
const windowsStartCandidates = (program, args = []) => [
|
|
212
|
+
['cmd', ['/c', 'start', '', program, ...args]]
|
|
213
|
+
];
|
|
210
214
|
const resolveSwiftExecutable = () => {
|
|
211
215
|
const directSwift = '/usr/bin/swift';
|
|
212
216
|
if (existsSync(directSwift)) {
|
|
@@ -295,16 +299,47 @@ const openGraphInAppWindow = (url) => {
|
|
|
295
299
|
}
|
|
296
300
|
if (platform() === 'win32') {
|
|
297
301
|
const appArgument = `--app=${url}`;
|
|
298
|
-
return (
|
|
299
|
-
|
|
300
|
-
|
|
302
|
+
return spawnAnyDetached([
|
|
303
|
+
...windowsStartCandidates('msedge', [appArgument, '--new-window']),
|
|
304
|
+
...windowsStartCandidates('chrome', [appArgument, '--new-window']),
|
|
305
|
+
...windowsStartCandidates('chromium', [appArgument, '--new-window']),
|
|
306
|
+
...windowsStartCandidates('brave', [appArgument, '--new-window'])
|
|
307
|
+
]);
|
|
301
308
|
}
|
|
302
309
|
const appArgument = `--app=${url}`;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
310
|
+
const linuxAppWindowCandidates = [
|
|
311
|
+
'microsoft-edge',
|
|
312
|
+
'microsoft-edge-stable',
|
|
313
|
+
'google-chrome',
|
|
314
|
+
'google-chrome-stable',
|
|
315
|
+
'chromium',
|
|
316
|
+
'chromium-browser',
|
|
317
|
+
'brave-browser'
|
|
318
|
+
].filter((candidate) => commandExists(candidate));
|
|
319
|
+
return spawnAnyDetached(linuxAppWindowCandidates.map((command) => [command, [appArgument, '--new-window']]));
|
|
320
|
+
};
|
|
321
|
+
const openGraphInDetectedBrowser = (url) => {
|
|
322
|
+
if (platform() === 'win32') {
|
|
323
|
+
return spawnAnyDetached([
|
|
324
|
+
...windowsStartCandidates('msedge', [url]),
|
|
325
|
+
...windowsStartCandidates('chrome', [url]),
|
|
326
|
+
...windowsStartCandidates('firefox', ['-new-window', url]),
|
|
327
|
+
...windowsStartCandidates('chromium', [url]),
|
|
328
|
+
...windowsStartCandidates('brave', [url])
|
|
329
|
+
]);
|
|
330
|
+
}
|
|
331
|
+
const linuxBrowserCandidates = [
|
|
332
|
+
['microsoft-edge', [url]],
|
|
333
|
+
['microsoft-edge-stable', [url]],
|
|
334
|
+
['google-chrome', [url]],
|
|
335
|
+
['google-chrome-stable', [url]],
|
|
336
|
+
['chromium', [url]],
|
|
337
|
+
['chromium-browser', [url]],
|
|
338
|
+
['brave-browser', [url]],
|
|
339
|
+
['firefox', ['-new-window', url]]
|
|
340
|
+
];
|
|
341
|
+
const available = linuxBrowserCandidates.filter(([command]) => commandExists(command));
|
|
342
|
+
return spawnAnyDetached(available);
|
|
308
343
|
};
|
|
309
344
|
const openUrlInUi = (url, parentPid) => {
|
|
310
345
|
const openDisabled = process.env.BRAINLINK_NO_BROWSER === '1' ||
|
|
@@ -326,6 +361,9 @@ const openUrlInUi = (url, parentPid) => {
|
|
|
326
361
|
if (platform() === 'darwin') {
|
|
327
362
|
return { opened: spawnDetached('open', [url]), mode: 'browser' };
|
|
328
363
|
}
|
|
364
|
+
if (openGraphInDetectedBrowser(url)) {
|
|
365
|
+
return { opened: true, mode: 'browser' };
|
|
366
|
+
}
|
|
329
367
|
if (platform() === 'win32') {
|
|
330
368
|
return { opened: spawnDetached('cmd', ['/c', 'start', '', url]), mode: 'browser' };
|
|
331
369
|
}
|
package/docs/AGENT_USAGE.md
CHANGED
|
@@ -555,6 +555,7 @@ Without `--vault`, the graph UI serves `$HOME/.brainlink/vault`.
|
|
|
555
555
|
The frontend includes an agent selector that shows only the agent id. Selecting an agent calls the same read APIs with `agent=<agent-id>` and renders that namespace instead of merging every agent into one graph.
|
|
556
556
|
|
|
557
557
|
Graph navigation controls include zoom in, zoom out, fit visible nodes and reset-to-fit-all nodes. Mouse wheel zoom (including `cmd+scroll` and `ctrl+scroll`) is anchored to the cursor. Keyboard shortcuts are `+` (zoom in), `-` (zoom out) and `0` (reset fit). Double-click on canvas zooms in at cursor position. Totals for notes, links and tags stay visible as floating metrics under the Brainlink title, and node details open on click in a modal (tags, outgoing links, backlinks and Markdown content).
|
|
558
|
+
During graph filtering, Brainlink keeps hub context nodes visible (`Memory Hub`/`MOC`/high-degree fallback) so filtered views still show relationship anchors.
|
|
558
559
|
|
|
559
560
|
The command reindexes by default, then serves:
|
|
560
561
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andespindola/brainlink",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.37",
|
|
4
4
|
"description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"author": "
|
|
7
|
+
"author": "Substructa",
|
|
8
8
|
"homepage": "https://github.com/andersonflima/brainlink#readme",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|