feat: miller column browser, offline cache, search, breadcrumbs
- Dynamic miller column sizing with flexbox layout - Root column (160px), middle slices (48px), active column (flex) - Auto-scroll browser to rightmost column on navigation - Offline indicator UI and IndexedDB cache integration - /api/documents endpoint for client-side document caching - Breadcrumb navigation in document content area - FTS5 search with SQLite virtual table - Client-side Mermaid and KaTeX rendering via CDN - Deep sample content (advanced-topics/raft-deep-dive) - Border removal (only search button keeps radius) - Full viewport layout with proper borders between sidebar/content
This commit is contained in:
@@ -85,14 +85,53 @@ func migrationApplied(ctx context.Context, db *sql.DB, version string) (bool, er
|
||||
}
|
||||
|
||||
func splitStatements(body string) []string {
|
||||
raw := strings.Split(body, ";")
|
||||
statements := make([]string, 0, len(raw))
|
||||
for _, statement := range raw {
|
||||
statement = strings.TrimSpace(statement)
|
||||
if statement == "" {
|
||||
var statements []string
|
||||
var current strings.Builder
|
||||
inTrigger := false
|
||||
depth := 0
|
||||
|
||||
lines := strings.Split(body, "\n")
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "" || strings.HasPrefix(trimmed, "--") {
|
||||
continue
|
||||
}
|
||||
statements = append(statements, statement)
|
||||
|
||||
upper := strings.ToUpper(trimmed)
|
||||
if strings.HasPrefix(upper, "CREATE TRIGGER") {
|
||||
inTrigger = true
|
||||
}
|
||||
|
||||
if inTrigger {
|
||||
current.WriteString(line)
|
||||
current.WriteString("\n")
|
||||
if strings.HasPrefix(upper, "BEGIN") {
|
||||
depth++
|
||||
}
|
||||
if strings.HasPrefix(upper, "END") {
|
||||
depth--
|
||||
if depth == 0 {
|
||||
inTrigger = false
|
||||
statements = append(statements, strings.TrimSpace(current.String()))
|
||||
current.Reset()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current.WriteString(line)
|
||||
current.WriteString("\n")
|
||||
if strings.HasSuffix(trimmed, ";") {
|
||||
statements = append(statements, strings.TrimSpace(current.String()))
|
||||
current.Reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if current.Len() > 0 {
|
||||
stmt := strings.TrimSpace(current.String())
|
||||
if stmt != "" {
|
||||
statements = append(statements, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
return statements
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS document_search;
|
||||
29
apps/server/internal/database/migrations/000007_fts5.up.sql
Normal file
29
apps/server/internal/database/migrations/000007_fts5.up.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS document_search USING fts5(
|
||||
title,
|
||||
content,
|
||||
path UNINDEXED,
|
||||
tokenize='porter'
|
||||
);
|
||||
|
||||
INSERT INTO document_search (rowid, title, content, path)
|
||||
SELECT rowid, title, '', path FROM documents;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS document_search_insert
|
||||
AFTER INSERT ON documents
|
||||
BEGIN
|
||||
INSERT INTO document_search (rowid, title, content, path)
|
||||
VALUES (NEW.rowid, NEW.title, '', NEW.path);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS document_search_update
|
||||
AFTER UPDATE ON documents
|
||||
BEGIN
|
||||
UPDATE document_search SET title = NEW.title, path = NEW.path
|
||||
WHERE rowid = NEW.rowid;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS document_search_delete
|
||||
AFTER DELETE ON documents
|
||||
BEGIN
|
||||
DELETE FROM document_search WHERE rowid = OLD.rowid;
|
||||
END;
|
||||
Reference in New Issue
Block a user