ex-brain 0.4.0 → 0.4.1
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/package.json +1 -1
- package/src/commands/graph-cmd.ts +38 -12
- package/src/commands/put-cmd.ts +10 -1
- package/src/repositories/brain-repo.ts +5 -1
- package/src/slug-utils.ts +12 -1
package/package.json
CHANGED
|
@@ -11,6 +11,20 @@ interface GraphNode {
|
|
|
11
11
|
group: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Normalize a type value. Slug-like values (no `/` in the original slug,
|
|
16
|
+
* contain `_`, or start with digits) are mapped to "article" so the filter
|
|
17
|
+
* panel doesn't list every individual document as its own type.
|
|
18
|
+
*/
|
|
19
|
+
function normalizeType(rawType: string, slug: string): string {
|
|
20
|
+
// If the raw type equals the slug's basename, it was inferred from a flat slug
|
|
21
|
+
const baseName = slug.includes("/") ? slug.split("/").pop()! : slug;
|
|
22
|
+
if (rawType === baseName || /^\d/.test(rawType) || rawType.startsWith("rm_")) {
|
|
23
|
+
return "article";
|
|
24
|
+
}
|
|
25
|
+
return rawType;
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
interface GraphEdge {
|
|
15
29
|
from: string;
|
|
16
30
|
to: string;
|
|
@@ -43,7 +57,8 @@ async function getGraphData(repo: BrainRepository): Promise<GraphData> {
|
|
|
43
57
|
|
|
44
58
|
// Create nodes from pages
|
|
45
59
|
for (const page of pages) {
|
|
46
|
-
const
|
|
60
|
+
const rawType = page.type || "other";
|
|
61
|
+
const type = normalizeType(rawType, page.slug);
|
|
47
62
|
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
|
48
63
|
|
|
49
64
|
nodes.push({
|
|
@@ -693,6 +708,10 @@ function getGraphHtml(): string {
|
|
|
693
708
|
const response = await fetch('/api/graph');
|
|
694
709
|
graphData = await response.json();
|
|
695
710
|
|
|
711
|
+
// Precompute node type map for O(1) edge visibility check
|
|
712
|
+
nodeTypeMap = new Map();
|
|
713
|
+
graphData.nodes.forEach(n => nodeTypeMap.set(n.id, n.type));
|
|
714
|
+
|
|
696
715
|
updateStats();
|
|
697
716
|
renderFilters();
|
|
698
717
|
renderNodeList();
|
|
@@ -837,22 +856,29 @@ function getGraphHtml(): string {
|
|
|
837
856
|
});
|
|
838
857
|
}
|
|
839
858
|
|
|
859
|
+
// Node type lookup for O(1) edge visibility check
|
|
860
|
+
let nodeTypeMap = new Map();
|
|
861
|
+
|
|
840
862
|
function updateNetworkVisibility() {
|
|
841
863
|
if (!nodes) return;
|
|
842
864
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
865
|
+
// Batch update nodes
|
|
866
|
+
const nodeUpdates = graphData.nodes.map(node => ({
|
|
867
|
+
id: node.id,
|
|
868
|
+
hidden: !activeTypes.has(node.type),
|
|
869
|
+
}));
|
|
870
|
+
nodes.update(nodeUpdates);
|
|
847
871
|
|
|
848
|
-
//
|
|
849
|
-
graphData.edges.
|
|
850
|
-
const
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
872
|
+
// Batch update edges with O(1) lookup
|
|
873
|
+
const edgeUpdates = graphData.edges.map(edge => {
|
|
874
|
+
const fromType = nodeTypeMap.get(edge.from);
|
|
875
|
+
const toType = nodeTypeMap.get(edge.to);
|
|
876
|
+
return {
|
|
877
|
+
id: edge.from + '->' + edge.to,
|
|
878
|
+
hidden: !activeTypes.has(fromType) || !activeTypes.has(toType),
|
|
879
|
+
};
|
|
855
880
|
});
|
|
881
|
+
edges.update(edgeUpdates);
|
|
856
882
|
}
|
|
857
883
|
|
|
858
884
|
async function selectNode(slug) {
|
package/src/commands/put-cmd.ts
CHANGED
|
@@ -498,10 +498,12 @@ Examples:
|
|
|
498
498
|
)
|
|
499
499
|
.action(async (opts: Record<string, string | undefined>) => {
|
|
500
500
|
await withRepo(program, async (repo) => {
|
|
501
|
+
const rawLimit = Number(opts.limit ?? 50);
|
|
502
|
+
const limit = (Number.isFinite(rawLimit) && rawLimit > 0) ? rawLimit : 50;
|
|
501
503
|
const rows = await repo.listPages({
|
|
502
504
|
type: opts.type,
|
|
503
505
|
tag: opts.tag,
|
|
504
|
-
limit
|
|
506
|
+
limit,
|
|
505
507
|
});
|
|
506
508
|
|
|
507
509
|
// When --fields is set, show one page per line with tab-separated values
|
|
@@ -516,9 +518,16 @@ Examples:
|
|
|
516
518
|
});
|
|
517
519
|
console.log(vals.join("\t"));
|
|
518
520
|
}
|
|
521
|
+
// Show count for tabular output too
|
|
522
|
+
if (!isJson(program) && rows.length >= limit) {
|
|
523
|
+
process.stderr.write(`\nShowing ${rows.length} page(s) (use --limit to show more)\n`);
|
|
524
|
+
}
|
|
519
525
|
return;
|
|
520
526
|
}
|
|
521
527
|
|
|
528
|
+
if (!isJson(program) && rows.length >= limit) {
|
|
529
|
+
process.stderr.write(`Showing ${rows.length} page(s) (use --limit to show more)\n`);
|
|
530
|
+
}
|
|
522
531
|
print(program, rows);
|
|
523
532
|
});
|
|
524
533
|
});
|
|
@@ -125,7 +125,11 @@ export class BrainRepository {
|
|
|
125
125
|
limit?: number;
|
|
126
126
|
}): Promise<PageRecord[]> {
|
|
127
127
|
try {
|
|
128
|
-
|
|
128
|
+
// Safe default: use 50 if limit is missing, NaN, non-finite, or <= 0
|
|
129
|
+
const rawLimit = filters.limit;
|
|
130
|
+
const limit = (typeof rawLimit === 'number' && Number.isFinite(rawLimit) && rawLimit > 0)
|
|
131
|
+
? rawLimit
|
|
132
|
+
: 50;
|
|
129
133
|
const params: unknown[] = [];
|
|
130
134
|
let sql = `SELECT p.slug, p.type, p.title, p.compiled_truth, p.timeline, p.frontmatter, p.created_at, p.updated_at
|
|
131
135
|
FROM pages p`;
|
package/src/slug-utils.ts
CHANGED
|
@@ -17,8 +17,19 @@ export function slugToTitle(slug: string): string {
|
|
|
17
17
|
.join(" ");
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Infer page type from slug path.
|
|
22
|
+
* - Slugs with a path prefix (e.g. "notes/my-post") → use the prefix as type
|
|
23
|
+
* - Flat slugs without "/" (e.g. "26_05_20_xxx" or "rm_hui_yi_ji_yao_0325") → default to "article"
|
|
24
|
+
* - Fallback to "other" if empty
|
|
25
|
+
*/
|
|
20
26
|
export function inferTypeFromSlug(slug: string): string {
|
|
21
|
-
|
|
27
|
+
const segments = slug.split("/");
|
|
28
|
+
if (segments.length > 1 && segments[0]) {
|
|
29
|
+
return segments[0];
|
|
30
|
+
}
|
|
31
|
+
// Flat slug — treat as a generic article/note
|
|
32
|
+
return "article";
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
/**
|