mirror of
https://github.com/saymrwulf/KnowledgeRefinery.git
synced 2026-05-14 20:47:51 +00:00
All docs, README, and presentation now reflect the Go daemon architecture: Python/FastAPI/LanceDB/PyMuPDF references replaced with Go/chi/SQLite/pdftotext. Updated test counts (97), model names (qwen3-4b-2507), app bundle structure, installer steps, and tech stack tables.
1574 lines
63 KiB
HTML
1574 lines
63 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Knowledge Refinery — Presentation</title>
|
|
<style>
|
|
/* ============================================================
|
|
RESET & BASE
|
|
============================================================ */
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
:root {
|
|
--bg: #ffffff;
|
|
--bg-subtle: #f8f9fc;
|
|
--bg-muted: #f0f2f7;
|
|
--bg-card: #ffffff;
|
|
--fg: #1a1d26;
|
|
--fg-muted: #5a6072;
|
|
--fg-subtle: #8891a5;
|
|
--accent: #4f46e5;
|
|
--accent-2: #7c3aed;
|
|
--accent-3: #2563eb;
|
|
--accent-4: #0891b2;
|
|
--accent-5: #059669;
|
|
--accent-bg: #eef2ff;
|
|
--gradient: linear-gradient(135deg, #4f46e5, #7c3aed, #2563eb);
|
|
--border: #e5e7eb;
|
|
--shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
|
|
--shadow-lg: 0 10px 40px rgba(0,0,0,0.08);
|
|
--radius: 12px;
|
|
--radius-sm: 8px;
|
|
--radius-xs: 6px;
|
|
--font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", system-ui, sans-serif;
|
|
--mono: "SF Mono", "Fira Code", "JetBrains Mono", ui-monospace, monospace;
|
|
--slide-w: 100vw;
|
|
--slide-h: 100vh;
|
|
}
|
|
|
|
html { font-size: 18px; scroll-behavior: smooth; }
|
|
|
|
body {
|
|
font-family: var(--font);
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
line-height: 1.6;
|
|
-webkit-font-smoothing: antialiased;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ============================================================
|
|
SLIDE ENGINE
|
|
============================================================ */
|
|
.deck {
|
|
width: var(--slide-w);
|
|
height: var(--slide-h);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.slide {
|
|
position: absolute;
|
|
top: 0; left: 0;
|
|
width: 100%; height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 4rem 6rem;
|
|
opacity: 0;
|
|
transform: translateX(60px);
|
|
transition: opacity 0.5s ease, transform 0.5s ease;
|
|
pointer-events: none;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.slide.active {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.slide.prev {
|
|
opacity: 0;
|
|
transform: translateX(-60px);
|
|
}
|
|
|
|
/* ============================================================
|
|
NAVIGATION
|
|
============================================================ */
|
|
.nav-bar {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0; right: 0;
|
|
height: 56px;
|
|
background: var(--bg);
|
|
border-top: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 2rem;
|
|
z-index: 100;
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
}
|
|
|
|
.nav-bar .progress {
|
|
flex: 1;
|
|
height: 4px;
|
|
background: var(--bg-muted);
|
|
border-radius: 2px;
|
|
margin: 0 1.5rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.nav-bar .progress-fill {
|
|
height: 100%;
|
|
background: var(--gradient);
|
|
border-radius: 2px;
|
|
transition: width 0.4s ease;
|
|
}
|
|
|
|
.nav-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
padding: 0.5rem 1rem;
|
|
border: none;
|
|
border-radius: var(--radius-xs);
|
|
background: var(--bg-muted);
|
|
color: var(--fg);
|
|
font: inherit;
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: background 0.2s, transform 0.1s;
|
|
user-select: none;
|
|
-webkit-user-select: none;
|
|
}
|
|
|
|
.nav-btn:hover { background: var(--border); }
|
|
.nav-btn:active { transform: scale(0.97); }
|
|
.nav-btn:disabled { opacity: 0.3; cursor: default; }
|
|
|
|
.slide-counter {
|
|
font-size: 0.8rem;
|
|
color: var(--fg-subtle);
|
|
font-variant-numeric: tabular-nums;
|
|
min-width: 4rem;
|
|
text-align: center;
|
|
}
|
|
|
|
/* ============================================================
|
|
TYPOGRAPHY
|
|
============================================================ */
|
|
.slide-inner { max-width: 1100px; width: 100%; }
|
|
|
|
h1 {
|
|
font-size: 3rem;
|
|
font-weight: 800;
|
|
letter-spacing: -0.03em;
|
|
line-height: 1.15;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
h1.gradient {
|
|
background: var(--gradient);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 2.2rem;
|
|
font-weight: 700;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1.2;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1.3rem;
|
|
font-weight: 600;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.subtitle {
|
|
font-size: 1.3rem;
|
|
color: var(--fg-muted);
|
|
font-weight: 400;
|
|
line-height: 1.5;
|
|
max-width: 700px;
|
|
}
|
|
|
|
.lead {
|
|
font-size: 1.15rem;
|
|
color: var(--fg-muted);
|
|
line-height: 1.7;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
/* ============================================================
|
|
COMPONENTS
|
|
============================================================ */
|
|
.tag {
|
|
display: inline-block;
|
|
padding: 0.2rem 0.7rem;
|
|
border-radius: 999px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.03em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.tag-accent { background: var(--accent-bg); color: var(--accent); }
|
|
.tag-green { background: #ecfdf5; color: #059669; }
|
|
.tag-amber { background: #fffbeb; color: #d97706; }
|
|
.tag-cyan { background: #ecfeff; color: #0891b2; }
|
|
.tag-rose { background: #fff1f2; color: #e11d48; }
|
|
|
|
.badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
padding: 0.35rem 0.8rem;
|
|
border-radius: var(--radius-xs);
|
|
font-size: 0.8rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Card grid */
|
|
.card-grid {
|
|
display: grid;
|
|
gap: 1.25rem;
|
|
width: 100%;
|
|
}
|
|
|
|
.card-grid.cols-2 { grid-template-columns: 1fr 1fr; }
|
|
.card-grid.cols-3 { grid-template-columns: 1fr 1fr 1fr; }
|
|
.card-grid.cols-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
|
|
|
|
.card {
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 1.5rem;
|
|
box-shadow: var(--shadow);
|
|
transition: box-shadow 0.25s, transform 0.25s;
|
|
}
|
|
|
|
.card:hover {
|
|
box-shadow: var(--shadow-lg);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.card .icon {
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.3rem;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.card .icon.indigo { background: #eef2ff; color: #4f46e5; }
|
|
.card .icon.violet { background: #f5f3ff; color: #7c3aed; }
|
|
.card .icon.blue { background: #eff6ff; color: #2563eb; }
|
|
.card .icon.cyan { background: #ecfeff; color: #0891b2; }
|
|
.card .icon.emerald { background: #ecfdf5; color: #059669; }
|
|
.card .icon.amber { background: #fffbeb; color: #d97706; }
|
|
.card .icon.rose { background: #fff1f2; color: #e11d48; }
|
|
.card .icon.slate { background: #f1f5f9; color: #475569; }
|
|
|
|
.card p { font-size: 0.9rem; color: var(--fg-muted); line-height: 1.55; }
|
|
|
|
/* Code block */
|
|
.code-block {
|
|
background: #1e1e2e;
|
|
color: #cdd6f4;
|
|
border-radius: var(--radius);
|
|
padding: 1.25rem 1.5rem;
|
|
font-family: var(--mono);
|
|
font-size: 0.82rem;
|
|
line-height: 1.65;
|
|
overflow-x: auto;
|
|
white-space: pre;
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
.code-block .comment { color: #6c7086; }
|
|
.code-block .keyword { color: #cba6f7; }
|
|
.code-block .string { color: #a6e3a1; }
|
|
.code-block .number { color: #fab387; }
|
|
.code-block .fn { color: #89b4fa; }
|
|
.code-block .var { color: #f5e0dc; }
|
|
.code-block .op { color: #89dceb; }
|
|
|
|
/* Pipeline visual */
|
|
.pipeline {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0;
|
|
width: 100%;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.pipeline-stage {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 1rem 0.5rem;
|
|
position: relative;
|
|
}
|
|
|
|
.pipeline-stage .num {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 700;
|
|
font-size: 0.85rem;
|
|
margin-bottom: 0.4rem;
|
|
color: white;
|
|
}
|
|
|
|
.pipeline-stage .label {
|
|
display: block;
|
|
font-weight: 600;
|
|
font-size: 0.85rem;
|
|
margin-bottom: 0.2rem;
|
|
}
|
|
|
|
.pipeline-stage .desc {
|
|
font-size: 0.72rem;
|
|
color: var(--fg-subtle);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.pipeline-arrow {
|
|
width: 32px;
|
|
flex-shrink: 0;
|
|
text-align: center;
|
|
color: var(--fg-subtle);
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
/* Architecture diagram */
|
|
.arch-diagram {
|
|
display: grid;
|
|
grid-template-columns: 1fr auto 1fr;
|
|
gap: 1.5rem;
|
|
align-items: stretch;
|
|
width: 100%;
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
.arch-box {
|
|
border: 2px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 1.25rem;
|
|
position: relative;
|
|
}
|
|
|
|
.arch-box h4 {
|
|
font-size: 0.8rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--fg-subtle);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.arch-connector {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--fg-subtle);
|
|
font-size: 1.5rem;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.arch-connector .proto {
|
|
font-size: 0.65rem;
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
letter-spacing: 0.05em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* Step list */
|
|
.step-list {
|
|
list-style: none;
|
|
counter-reset: steps;
|
|
}
|
|
|
|
.step-list li {
|
|
counter-increment: steps;
|
|
padding: 1rem 1rem 1rem 3.5rem;
|
|
position: relative;
|
|
border-left: 2px solid var(--border);
|
|
margin-left: 1rem;
|
|
}
|
|
|
|
.step-list li:last-child { border-left-color: transparent; }
|
|
|
|
.step-list li::before {
|
|
content: counter(steps);
|
|
position: absolute;
|
|
left: -14px;
|
|
top: 1rem;
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
background: var(--accent);
|
|
color: white;
|
|
font-weight: 700;
|
|
font-size: 0.8rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.step-list li strong {
|
|
display: block;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.step-list li p {
|
|
font-size: 0.9rem;
|
|
color: var(--fg-muted);
|
|
}
|
|
|
|
/* Table */
|
|
.data-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.data-table th {
|
|
text-align: left;
|
|
padding: 0.6rem 1rem;
|
|
border-bottom: 2px solid var(--border);
|
|
color: var(--fg-subtle);
|
|
font-weight: 600;
|
|
font-size: 0.75rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.data-table td {
|
|
padding: 0.6rem 1rem;
|
|
border-bottom: 1px solid var(--bg-muted);
|
|
vertical-align: top;
|
|
}
|
|
|
|
.data-table tr:last-child td { border-bottom: none; }
|
|
.data-table code {
|
|
font-family: var(--mono);
|
|
font-size: 0.8rem;
|
|
background: var(--bg-muted);
|
|
padding: 0.1rem 0.4rem;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* Title slide decoration */
|
|
.title-slide {
|
|
position: relative;
|
|
background: var(--bg-subtle);
|
|
}
|
|
|
|
.title-slide::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: -200px; right: -200px;
|
|
width: 600px; height: 600px;
|
|
border-radius: 50%;
|
|
background: radial-gradient(circle, rgba(79,70,229,0.06) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.title-slide::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: -150px; left: -150px;
|
|
width: 500px; height: 500px;
|
|
border-radius: 50%;
|
|
background: radial-gradient(circle, rgba(124,58,237,0.05) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Feature highlight */
|
|
.feature-row {
|
|
display: flex;
|
|
gap: 2rem;
|
|
align-items: flex-start;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.feature-row .feature-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.4rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.feature-row .feature-text h3 {
|
|
margin-bottom: 0.3rem;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.feature-row .feature-text p {
|
|
font-size: 0.9rem;
|
|
color: var(--fg-muted);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Two-col layout */
|
|
.two-col {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 2.5rem;
|
|
width: 100%;
|
|
align-items: start;
|
|
}
|
|
|
|
/* Stats row */
|
|
.stats-row {
|
|
display: flex;
|
|
gap: 2rem;
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
.stat {
|
|
text-align: center;
|
|
}
|
|
|
|
.stat .stat-num {
|
|
font-size: 2.5rem;
|
|
font-weight: 800;
|
|
background: var(--gradient);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
line-height: 1;
|
|
}
|
|
|
|
.stat .stat-label {
|
|
font-size: 0.8rem;
|
|
color: var(--fg-subtle);
|
|
font-weight: 500;
|
|
margin-top: 0.3rem;
|
|
}
|
|
|
|
/* Annotation example */
|
|
.annotation-example {
|
|
background: var(--bg-subtle);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 1.5rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.annotation-example .ann-field {
|
|
margin-bottom: 0.6rem;
|
|
}
|
|
|
|
.annotation-example .ann-label {
|
|
font-weight: 600;
|
|
font-size: 0.75rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: var(--fg-subtle);
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.annotation-example .ann-tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.ann-tag {
|
|
padding: 0.15rem 0.55rem;
|
|
border-radius: 999px;
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Extractor matrix */
|
|
.extractor-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 1rem;
|
|
}
|
|
|
|
.extractor-item {
|
|
background: var(--bg-subtle);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-sm);
|
|
padding: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.extractor-item .ext-icon {
|
|
font-size: 1.8rem;
|
|
margin-bottom: 0.4rem;
|
|
}
|
|
|
|
.extractor-item .ext-name {
|
|
font-weight: 600;
|
|
font-size: 0.85rem;
|
|
margin-bottom: 0.2rem;
|
|
}
|
|
|
|
.extractor-item .ext-types {
|
|
font-size: 0.72rem;
|
|
color: var(--fg-subtle);
|
|
}
|
|
|
|
/* Keyboard hint */
|
|
.keyboard-hint {
|
|
position: fixed;
|
|
top: 1rem;
|
|
right: 1.5rem;
|
|
font-size: 0.72rem;
|
|
color: var(--fg-subtle);
|
|
z-index: 50;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.key {
|
|
display: inline-block;
|
|
padding: 0.1rem 0.45rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
font-family: var(--mono);
|
|
font-size: 0.7rem;
|
|
background: var(--bg-subtle);
|
|
box-shadow: 0 1px 0 var(--border);
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 900px) {
|
|
.slide { padding: 2.5rem 2rem; }
|
|
h1 { font-size: 2.2rem; }
|
|
h2 { font-size: 1.7rem; }
|
|
.card-grid.cols-3 { grid-template-columns: 1fr 1fr; }
|
|
.card-grid.cols-4 { grid-template-columns: 1fr 1fr; }
|
|
.two-col { grid-template-columns: 1fr; }
|
|
.pipeline { flex-wrap: wrap; }
|
|
.arch-diagram { grid-template-columns: 1fr; }
|
|
.extractor-grid { grid-template-columns: 1fr 1fr; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="keyboard-hint"><span class="key">←</span> <span class="key">→</span> or swipe to navigate</div>
|
|
|
|
<div class="deck" id="deck">
|
|
|
|
<!-- ============================================================
|
|
SLIDE 1: TITLE
|
|
============================================================ -->
|
|
<div class="slide title-slide active" data-slide="0">
|
|
<div class="slide-inner" style="text-align:center;">
|
|
<div style="margin-bottom:1rem;">
|
|
<span class="tag tag-accent">Local-First · Evidence-Native · LLM-Powered</span>
|
|
</div>
|
|
<h1 class="gradient" style="font-size:3.5rem;">Knowledge Refinery</h1>
|
|
<p class="subtitle" style="margin:0 auto 2rem;">
|
|
A macOS Tahoe application that ingests heterogeneous document corpora,<br>
|
|
extracts structured knowledge via local LLMs, and renders an immersive 3D concept universe.
|
|
</p>
|
|
<div class="stats-row" style="justify-content:center;">
|
|
<div class="stat"><div class="stat-num">7</div><div class="stat-label">Milestones</div></div>
|
|
<div class="stat"><div class="stat-num">6</div><div class="stat-label">Pipeline Stages</div></div>
|
|
<div class="stat"><div class="stat-num">30</div><div class="stat-label">Unit Tests</div></div>
|
|
<div class="stat"><div class="stat-num">0</div><div class="stat-label">Cloud Calls</div></div>
|
|
</div>
|
|
<div style="margin-top:2rem; color:var(--fg-subtle); font-size:0.85rem;">
|
|
macOS · SwiftUI · Go · chi · LM Studio · SQLite · WebGPU · Multi-Workspace
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 2: THE PROBLEM
|
|
============================================================ -->
|
|
<div class="slide" data-slide="1">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-amber" style="margin-bottom:1rem;">The Problem</span>
|
|
<h2>Your knowledge is scattered, siloed, and unsearchable</h2>
|
|
<p class="lead">
|
|
Research papers, notes, e-books, medical images, code, archives — spread across folders
|
|
with no semantic connections. Keyword search fails. Cloud tools leak your data. You need
|
|
something that actually <em>understands</em> your corpus.
|
|
</p>
|
|
<div class="card-grid cols-3">
|
|
<div class="card">
|
|
<div class="icon rose">🔍</div>
|
|
<h3>Search is Broken</h3>
|
|
<p>Keyword search misses semantically related content. "Machine learning" won't find your "neural network" papers.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon amber">🔒</div>
|
|
<h3>Privacy Concerns</h3>
|
|
<p>Cloud-based tools send your sensitive documents, medical records, and proprietary research to third-party servers.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon slate">📁</div>
|
|
<h3>No Connections</h3>
|
|
<p>You have thousands of files but no way to see how concepts relate across documents, formats, and domains.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 3: THE SOLUTION
|
|
============================================================ -->
|
|
<div class="slide" data-slide="2">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-green" style="margin-bottom:1rem;">The Solution</span>
|
|
<h2>Knowledge Refinery: Your Local Knowledge Engine</h2>
|
|
<div class="feature-row">
|
|
<div class="feature-icon" style="background:#eef2ff; color:#4f46e5;">🧠</div>
|
|
<div class="feature-text">
|
|
<h3>Semantic Understanding via Local LLMs</h3>
|
|
<p>Uses LM Studio running locally — your data never leaves your machine. Embeddings for similarity search, structured annotation for deep understanding.</p>
|
|
</div>
|
|
</div>
|
|
<div class="feature-row">
|
|
<div class="feature-icon" style="background:#ecfdf5; color:#059669;">🔗</div>
|
|
<div class="feature-text">
|
|
<h3>Evidence-Native — Every Insight Links to Source</h3>
|
|
<p>Every annotation, concept, and search result traces back to an exact location: file, page, chapter, even byte offset within nested archives.</p>
|
|
</div>
|
|
</div>
|
|
<div class="feature-row">
|
|
<div class="feature-icon" style="background:#ecfeff; color:#0891b2;">🌌</div>
|
|
<div class="feature-text">
|
|
<h3>3D Concept Universe</h3>
|
|
<p>WebGPU-powered visualization renders your knowledge as an interactive 3D graph. Zoom from macro concepts to individual chunks. See how ideas connect.</p>
|
|
</div>
|
|
</div>
|
|
<div class="feature-row">
|
|
<div class="feature-icon" style="background:#fff1f2; color:#e11d48;">🛡</div>
|
|
<div class="feature-text">
|
|
<h3>Sandboxed & Incremental</h3>
|
|
<p>Archive extraction runs in macOS sandbox-exec. Changed files are detected via content hashing — only new/modified files are reprocessed.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 4: ARCHITECTURE
|
|
============================================================ -->
|
|
<div class="slide" data-slide="3">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">Architecture</span>
|
|
<h2>Three-Tier Local Architecture</h2>
|
|
<div class="arch-diagram">
|
|
<div class="arch-box" style="border-color:#4f46e5;">
|
|
<h4 style="color:#4f46e5;">📱 SwiftUI App</h4>
|
|
<div style="font-size:0.85rem; color:var(--fg-muted);">
|
|
<strong>Search</strong> — Semantic vector search<br>
|
|
<strong>Universe</strong> — WebGPU 3D visualization<br>
|
|
<strong>Concepts</strong> — Cluster browser + "Why?"<br>
|
|
<strong>Ingest</strong> — Pipeline monitoring<br>
|
|
<strong>Volumes</strong> — Folder management<br>
|
|
<strong>Assets</strong> — File inventory + Quick Look
|
|
</div>
|
|
</div>
|
|
<div class="arch-connector">
|
|
<span>↔</span>
|
|
<span class="proto">HTTP (localhost)</span>
|
|
<span style="font-size:0.65rem; color:var(--fg-subtle);">127.0.0.1:8742</span>
|
|
</div>
|
|
<div class="arch-box" style="border-color:#7c3aed;">
|
|
<h4 style="color:#7c3aed;">⚙️ Go Daemon</h4>
|
|
<div style="font-size:0.85rem; color:var(--fg-muted);">
|
|
<strong>chi</strong> router with CORS<br>
|
|
<strong>6-Stage Pipeline</strong> orchestrator<br>
|
|
<strong>SQLite</strong> metadata + vectors + graph (WAL)<br>
|
|
<strong>11MB</strong> single binary, zero deps<br>
|
|
<strong>7 Extractors</strong> + fallback chain
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="text-align:center; margin-top:1rem;">
|
|
<span style="font-size:1.5rem; color:var(--fg-subtle);">↓</span>
|
|
<span class="proto" style="display:block; font-size:0.65rem; font-weight:600; color:var(--accent-4); letter-spacing:0.05em; text-transform:uppercase;">OpenAI-Compatible API</span>
|
|
</div>
|
|
<div style="max-width:360px; margin:0.75rem auto 0;">
|
|
<div class="arch-box" style="border-color:#059669;">
|
|
<h4 style="color:#059669;">🤖 LM Studio</h4>
|
|
<div style="font-size:0.85rem; color:var(--fg-muted);">
|
|
<strong>qwen3-4b-2507</strong> — Chat / Annotation<br>
|
|
<strong>nomic-embed-text-v1.5</strong> — Embeddings<br>
|
|
Running on 127.0.0.1:1234
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 5: THE PIPELINE
|
|
============================================================ -->
|
|
<div class="slide" data-slide="4">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">Core Engine</span>
|
|
<h2>Six-Stage Ingestion Pipeline</h2>
|
|
<p class="lead" style="margin-bottom:1rem;">Every file flows through six deterministic stages. The pipeline is incremental — unchanged files are never reprocessed.</p>
|
|
<div class="pipeline">
|
|
<div class="pipeline-stage">
|
|
<div class="num" style="background:#4f46e5;">1</div>
|
|
<span class="label">Scan</span>
|
|
<span class="desc">Walk directories, compute SHA-256 content hashes, detect new/changed files</span>
|
|
</div>
|
|
<div class="pipeline-arrow">→</div>
|
|
<div class="pipeline-stage">
|
|
<div class="num" style="background:#7c3aed;">2</div>
|
|
<span class="label">Extract</span>
|
|
<span class="desc">Produce ContentAtoms with evidence anchors (page, chapter, offset)</span>
|
|
</div>
|
|
<div class="pipeline-arrow">→</div>
|
|
<div class="pipeline-stage">
|
|
<div class="num" style="background:#2563eb;">3</div>
|
|
<span class="label">Chunk</span>
|
|
<span class="desc">Deterministic 500-800 token splits with 50-token overlap, stable IDs</span>
|
|
</div>
|
|
<div class="pipeline-arrow">→</div>
|
|
<div class="pipeline-stage">
|
|
<div class="num" style="background:#0891b2;">4</div>
|
|
<span class="label">Embed</span>
|
|
<span class="desc">768-dim vectors via nomic-embed-text stored in SQLite</span>
|
|
</div>
|
|
<div class="pipeline-arrow">→</div>
|
|
<div class="pipeline-stage">
|
|
<div class="num" style="background:#059669;">5</div>
|
|
<span class="label">Annotate</span>
|
|
<span class="desc">LLM extracts topics, entities, claims, sentiment, summary</span>
|
|
</div>
|
|
<div class="pipeline-arrow">→</div>
|
|
<div class="pipeline-stage">
|
|
<div class="num" style="background:#d97706;">6</div>
|
|
<span class="label">Conceptualize</span>
|
|
<span class="desc">K-means++ clustering, concept labeling, kNN similarity graph</span>
|
|
</div>
|
|
</div>
|
|
<div style="text-align:center; margin-top:1rem; font-size:0.8rem; color:var(--fg-subtle);">
|
|
Status tracked per-asset: <code>pending → extracted → chunked → embedded → annotated</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 6: EXTRACTORS
|
|
============================================================ -->
|
|
<div class="slide" data-slide="5">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-cyan" style="margin-bottom:1rem;">Format Support</span>
|
|
<h2>Seven Pluggable Extractors</h2>
|
|
<p class="lead">Priority-sorted extractor registry. Each extractor produces ContentAtoms with evidence anchors. The Tika fallback handles anything the others miss.</p>
|
|
<div class="extractor-grid">
|
|
<div class="extractor-item">
|
|
<div class="ext-icon">📑</div>
|
|
<div class="ext-name">PDF</div>
|
|
<div class="ext-types">.pdf · Per-page text · pdftotext</div>
|
|
<div><span class="tag tag-accent" style="font-size:0.6rem;">Priority 20</span></div>
|
|
</div>
|
|
<div class="extractor-item">
|
|
<div class="ext-icon">📖</div>
|
|
<div class="ext-name">EPUB</div>
|
|
<div class="ext-types">.epub · OPF spine order · Metadata</div>
|
|
<div><span class="tag tag-accent" style="font-size:0.6rem;">Priority 18</span></div>
|
|
</div>
|
|
<div class="extractor-item">
|
|
<div class="ext-icon">🖼</div>
|
|
<div class="ext-name">Image + OCR</div>
|
|
<div class="ext-types">.png .jpg .heic · macOS Vision</div>
|
|
<div><span class="tag tag-accent" style="font-size:0.6rem;">Priority 15</span></div>
|
|
</div>
|
|
<div class="extractor-item">
|
|
<div class="ext-icon">🩻</div>
|
|
<div class="ext-name">DICOM</div>
|
|
<div class="ext-types">.dcm · Medical metadata · Binary header parsing</div>
|
|
<div><span class="tag tag-accent" style="font-size:0.6rem;">Priority 15</span></div>
|
|
</div>
|
|
<div class="extractor-item">
|
|
<div class="ext-icon">📝</div>
|
|
<div class="ext-name">Text</div>
|
|
<div class="ext-types">.txt .md .html .rtf · Tag stripping</div>
|
|
<div><span class="tag tag-accent" style="font-size:0.6rem;">Priority 10</span></div>
|
|
</div>
|
|
<div class="extractor-item">
|
|
<div class="ext-icon">📦</div>
|
|
<div class="ext-name">Archive</div>
|
|
<div class="ext-types">.zip .tar.gz · Sandboxed · Zip-slip safe</div>
|
|
<div><span class="tag tag-accent" style="font-size:0.6rem;">Priority 5</span></div>
|
|
</div>
|
|
<div class="extractor-item">
|
|
<div class="ext-icon">🔄</div>
|
|
<div class="ext-name">Tika Fallback</div>
|
|
<div class="ext-types">.doc .docx .pptx · textutil · Raw text</div>
|
|
<div><span class="tag tag-accent" style="font-size:0.6rem;">Priority 1</span></div>
|
|
</div>
|
|
<div class="extractor-item" style="background:var(--bg-muted); border-style:dashed;">
|
|
<div class="ext-icon" style="opacity:0.4;">➕</div>
|
|
<div class="ext-name" style="color:var(--fg-subtle);">Custom</div>
|
|
<div class="ext-types">Extend BaseExtractor · Register in registry</div>
|
|
<div><span class="tag" style="font-size:0.6rem; background:var(--bg-muted); color:var(--fg-subtle);">Pluggable</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 7: ANNOTATIONS
|
|
============================================================ -->
|
|
<div class="slide" data-slide="6">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-green" style="margin-bottom:1rem;">LLM Intelligence</span>
|
|
<h2>Rich Structured Annotations</h2>
|
|
<p class="lead">Every chunk is annotated by the local LLM, producing structured metadata that enriches search and powers concept formation.</p>
|
|
<div class="two-col">
|
|
<div>
|
|
<div class="annotation-example">
|
|
<div class="ann-field">
|
|
<div class="ann-label">Topics</div>
|
|
<div class="ann-tags">
|
|
<span class="ann-tag" style="background:#eef2ff; color:#4f46e5;">cryptography</span>
|
|
<span class="ann-tag" style="background:#f5f3ff; color:#7c3aed;">encryption</span>
|
|
<span class="ann-tag" style="background:#eff6ff; color:#2563eb;">security</span>
|
|
<span class="ann-tag" style="background:#ecfeff; color:#0891b2;">post-quantum</span>
|
|
<span class="ann-tag" style="background:#ecfdf5; color:#059669;">digital signatures</span>
|
|
</div>
|
|
</div>
|
|
<div class="ann-field">
|
|
<div class="ann-label">Summary</div>
|
|
<p style="font-size:0.85rem; color:var(--fg-muted); font-style:italic;">
|
|
"This text describes various cryptographic techniques, including symmetric and asymmetric encryption algorithms like AES and RSA..."
|
|
</p>
|
|
</div>
|
|
<div class="ann-field">
|
|
<div class="ann-label">Entities</div>
|
|
<div class="ann-tags">
|
|
<span class="ann-tag" style="background:#fff1f2; color:#e11d48;">AES</span>
|
|
<span class="ann-tag" style="background:#fff1f2; color:#e11d48;">RSA</span>
|
|
<span class="ann-tag" style="background:#fff1f2; color:#e11d48;">ECC</span>
|
|
<span class="ann-tag" style="background:#fffbeb; color:#d97706;">NIST</span>
|
|
<span class="ann-tag" style="background:#fff1f2; color:#e11d48;">SHA-256</span>
|
|
<span class="ann-tag" style="background:#fff1f2; color:#e11d48;">EdDSA</span>
|
|
</div>
|
|
</div>
|
|
<div class="ann-field" style="margin-bottom:0;">
|
|
<div class="ann-label">Sentiment</div>
|
|
<span class="badge" style="background:#f0f2f7; color:#5a6072;">⚖ neutral · 0.95</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h3>Annotation Fields</h3>
|
|
<table class="data-table">
|
|
<tr><th>Field</th><th>Type</th></tr>
|
|
<tr><td><code>topics</code></td><td>2-5 multi-label tags</td></tr>
|
|
<tr><td><code>entities</code></td><td>Named entities + type</td></tr>
|
|
<tr><td><code>claims</code></td><td>Extracted claims + confidence</td></tr>
|
|
<tr><td><code>sentiment</code></td><td>Label + confidence score</td></tr>
|
|
<tr><td><code>summary</code></td><td>1-2 sentence summary</td></tr>
|
|
<tr><td><code>quality_flags</code></td><td>Truncated, technical, etc.</td></tr>
|
|
</table>
|
|
<div style="margin-top:1.25rem; padding:1rem; background:var(--bg-subtle); border-radius:var(--radius-sm); font-size:0.82rem; color:var(--fg-muted);">
|
|
<strong style="color:var(--fg);">Versioned & Immutable:</strong> Annotations are never overwritten. New model/prompt versions create new records with <code>is_current=1</code>, preserving the full audit trail.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 8: CONCEPT UNIVERSE
|
|
============================================================ -->
|
|
<div class="slide" data-slide="7">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">Visualization</span>
|
|
<h2>3D Concept Universe</h2>
|
|
<p class="lead">Concepts are rendered as a force-directed 3D graph using WebGPU. Orbit, zoom, and click to explore how your knowledge connects.</p>
|
|
<div class="two-col">
|
|
<div>
|
|
<div style="background:#0a0a14; border-radius:var(--radius); padding:2rem; text-align:center; position:relative; overflow:hidden;">
|
|
<!-- Simulated universe -->
|
|
<svg viewBox="0 0 400 280" style="width:100%; max-width:400px;">
|
|
<!-- Edges -->
|
|
<line x1="120" y1="90" x2="280" y2="120" stroke="#4f46e560" stroke-width="1"/>
|
|
<line x1="120" y1="90" x2="200" y2="210" stroke="#7c3aed40" stroke-width="1"/>
|
|
<line x1="280" y1="120" x2="200" y2="210" stroke="#2563eb40" stroke-width="1"/>
|
|
<line x1="120" y1="90" x2="80" y2="160" stroke="#4f46e530" stroke-width="0.5"/>
|
|
<line x1="120" y1="90" x2="160" y2="50" stroke="#4f46e530" stroke-width="0.5"/>
|
|
<line x1="280" y1="120" x2="320" y2="70" stroke="#2563eb30" stroke-width="0.5"/>
|
|
<line x1="280" y1="120" x2="340" y2="170" stroke="#2563eb30" stroke-width="0.5"/>
|
|
<line x1="200" y1="210" x2="160" y2="250" stroke="#7c3aed30" stroke-width="0.5"/>
|
|
<line x1="200" y1="210" x2="240" y2="260" stroke="#7c3aed30" stroke-width="0.5"/>
|
|
<!-- Concept nodes -->
|
|
<circle cx="120" cy="90" r="22" fill="#4f46e5" opacity="0.9"/>
|
|
<circle cx="280" cy="120" r="20" fill="#2563eb" opacity="0.9"/>
|
|
<circle cx="200" cy="210" r="18" fill="#7c3aed" opacity="0.9"/>
|
|
<!-- Chunk nodes -->
|
|
<circle cx="80" cy="160" r="5" fill="#4f46e580"/>
|
|
<circle cx="160" cy="50" r="5" fill="#4f46e580"/>
|
|
<circle cx="320" cy="70" r="5" fill="#2563eb80"/>
|
|
<circle cx="340" cy="170" r="5" fill="#2563eb80"/>
|
|
<circle cx="160" cy="250" r="5" fill="#7c3aed80"/>
|
|
<circle cx="240" cy="260" r="5" fill="#7c3aed80"/>
|
|
<!-- Labels -->
|
|
<text x="120" y="94" text-anchor="middle" fill="white" font-size="7" font-weight="600">AI Learning</text>
|
|
<text x="280" y="124" text-anchor="middle" fill="white" font-size="7" font-weight="600">Data Systems</text>
|
|
<text x="200" y="214" text-anchor="middle" fill="white" font-size="7" font-weight="600">Security</text>
|
|
</svg>
|
|
<div style="position:absolute; top:0.75rem; left:0.75rem; display:flex; gap:0.5rem;">
|
|
<span style="background:rgba(255,255,255,0.1); color:rgba(255,255,255,0.7); font-size:0.6rem; padding:0.2rem 0.5rem; border-radius:4px; backdrop-filter:blur(8px);">9 nodes</span>
|
|
<span style="background:rgba(255,255,255,0.1); color:rgba(255,255,255,0.7); font-size:0.6rem; padding:0.2rem 0.5rem; border-radius:4px;">MACRO</span>
|
|
<span style="background:rgba(255,255,255,0.1); color:rgba(255,255,255,0.7); font-size:0.6rem; padding:0.2rem 0.5rem; border-radius:4px;">60 fps</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h3>Rendering Engine</h3>
|
|
<table class="data-table">
|
|
<tr><td><strong>Renderer</strong></td><td>WebGPU in WKWebView</td></tr>
|
|
<tr><td><strong>Shaders</strong></td><td>WGSL (214 lines)</td></tr>
|
|
<tr><td><strong>Layout</strong></td><td>Force-directed (Velocity Verlet)</td></tr>
|
|
<tr><td><strong>Nodes</strong></td><td>Billboarded quads with circle SDF + glow</td></tr>
|
|
<tr><td><strong>Camera</strong></td><td>Orbit (drag), Pan (right-drag), Zoom (scroll)</td></tr>
|
|
</table>
|
|
<h3 style="margin-top:1.25rem;">Level-of-Detail System</h3>
|
|
<table class="data-table">
|
|
<tr><th>LOD</th><th>Shows</th><th>Zoom</th></tr>
|
|
<tr><td><span class="tag tag-accent" style="font-size:0.65rem;">MACRO</span></td><td>Concept clusters only</td><td>Distant</td></tr>
|
|
<tr><td><span class="tag tag-cyan" style="font-size:0.65rem;">MID</span></td><td>Concepts + sub-concepts</td><td>Medium</td></tr>
|
|
<tr><td><span class="tag tag-green" style="font-size:0.65rem;">NEAR</span></td><td>All nodes + all edges</td><td>Close</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 9: DATA MODEL
|
|
============================================================ -->
|
|
<div class="slide" data-slide="8">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">Data Model</span>
|
|
<h2>Evidence-Native Knowledge Graph</h2>
|
|
<p class="lead">Every derived artifact links back to its source. The data model captures the full provenance chain from file to concept.</p>
|
|
<div class="two-col">
|
|
<div>
|
|
<div class="code-block" style="font-size:0.72rem;">
|
|
<span class="comment">-- Provenance chain:</span>
|
|
WatchedVolume
|
|
<span class="op">↓</span> <span class="comment">(contains files)</span>
|
|
FileAsset <span class="comment">-- SHA-256 ID, content hash</span>
|
|
<span class="op">↓</span> <span class="comment">(extracted into)</span>
|
|
ContentAtom <span class="comment">-- text/image/table/metadata</span>
|
|
<span class="op">↓</span> <span class="comment">(split into)</span>
|
|
Chunk <span class="comment">-- deterministic ID, 500-800 tokens</span>
|
|
<span class="op">↓</span>
|
|
<span class="op">├──></span> Vector <span class="comment">(SQLite BLOB, 768-dim)</span>
|
|
<span class="op">├──></span> Annotation <span class="comment">(versioned, immutable)</span>
|
|
<span class="op">└──></span> GraphEdge
|
|
<span class="op">↓</span>
|
|
ConceptNode <span class="comment">(hierarchical clusters)</span></div>
|
|
</div>
|
|
<div>
|
|
<h3>Evidence Anchors</h3>
|
|
<p style="font-size:0.85rem; color:var(--fg-muted); margin-bottom:1rem;">Every ContentAtom, Chunk, and Edge stores a JSON evidence anchor linking to the exact source location.</p>
|
|
<div class="code-block" style="font-size:0.72rem;">
|
|
{
|
|
<span class="string">"asset_id"</span>: <span class="string">"abc123..."</span>,
|
|
<span class="string">"page"</span>: <span class="number">5</span>,
|
|
<span class="string">"chapter"</span>: <span class="string">"Introduction"</span>,
|
|
<span class="string">"archive_chain"</span>: <span class="string">"docs.zip/paper.pdf"</span>,
|
|
<span class="string">"line_start"</span>: <span class="number">42</span>,
|
|
<span class="string">"line_end"</span>: <span class="number">58</span>
|
|
}</div>
|
|
<h3 style="margin-top:1rem;">Storage</h3>
|
|
<table class="data-table" style="font-size:0.8rem;">
|
|
<tr><td><strong>SQLite</strong></td><td>Metadata, vectors (BLOB), graph, jobs (WAL mode)</td></tr>
|
|
<tr><td><strong>Disk</strong></td><td>~/.knowledge-refinery/</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 10: SECURITY
|
|
============================================================ -->
|
|
<div class="slide" data-slide="9">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-rose" style="margin-bottom:1rem;">Security</span>
|
|
<h2>Defense in Depth</h2>
|
|
<p class="lead">Zero cloud calls. Localhost-only daemon. Sandboxed extraction. Every security layer works locally.</p>
|
|
<div class="card-grid cols-3">
|
|
<div class="card">
|
|
<div class="icon indigo">🔐</div>
|
|
<h3>Localhost-Only Binding</h3>
|
|
<p>Daemon binds exclusively to <code>127.0.0.1</code> — not reachable from the network. No tokens needed for a single-user local app.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon violet">🛡</div>
|
|
<h3>macOS Sandbox</h3>
|
|
<p>Archive extraction runs in <code>sandbox-exec</code> with: no network, restricted filesystem, CPU and memory limits.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon blue">🚫</div>
|
|
<h3>Zip-Slip Prevention</h3>
|
|
<p>All archive member paths are validated and resolved against the extraction base directory. Traversal attempts are blocked.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon cyan">💣</div>
|
|
<h3>Archive Bomb Detection</h3>
|
|
<p>Limits enforced: 10,000 max files, 500MB total extracted, 50MB per file, max 3 nesting levels.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon emerald">💻</div>
|
|
<h3>100% Local</h3>
|
|
<p>LM Studio runs on localhost. Data stays on disk. No telemetry, no cloud APIs, no external network calls. Zero data leakage.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon amber">🔍</div>
|
|
<h3>Content Hashing</h3>
|
|
<p>SHA-256 streaming hash for change detection. Deterministic chunk IDs ensure stable references across re-processing.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 11: MASTER CONTROL APP
|
|
============================================================ -->
|
|
<div class="slide" data-slide="10">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">Milestone 7</span>
|
|
<h2>Master Control App</h2>
|
|
<p class="lead">One dashboard to manage everything. LM Studio monitoring, multi-workspace lifecycle, visual data lake mapping — all from a single window.</p>
|
|
<div class="card-grid cols-3">
|
|
<div class="card">
|
|
<div class="icon indigo">📊</div>
|
|
<h3>Dashboard</h3>
|
|
<p>LM Studio status card with model names. Workspace grid with start/stop toggles, vector counts, and color-coded cards. "Start All" for one-click launch.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon violet">📦</div>
|
|
<h3>Multi-Workspace</h3>
|
|
<p>Each workspace gets its own data directory, daemon port (8742, 8743, ...), and SQLite database (metadata + vectors + graph). Independent lifecycle management.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon blue">🗺</div>
|
|
<h3>Data Lake Mapping</h3>
|
|
<p>Visual Canvas view showing which folders feed which workspaces. Bézier curves connect data lakes to knowledge bases with color-coded workspace tags.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon cyan">🧠</div>
|
|
<h3>LM Studio Monitor</h3>
|
|
<p>Direct polling of <code>/v1/models</code> every 5 seconds. Auto-classifies chat vs. embedding models. Independent of daemon status — green means ready.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon emerald">🔧</div>
|
|
<h3>Daemon Lifecycle</h3>
|
|
<p>Start, stop, restart per-workspace daemons. Environment variables <code>KR_DATA_DIR</code> and <code>KR_PORT</code> injected automatically. Live log capture (last 500 lines).</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon amber">📁</div>
|
|
<h3>Workspace Setup</h3>
|
|
<p>Create workspaces with name, color tag, and folder picker. Native <code>NSOpenPanel</code> for multi-selecting data lake paths including external drives.</p>
|
|
</div>
|
|
</div>
|
|
<div style="margin-top:1.5rem; text-align:center;">
|
|
<div style="display:inline-flex; gap:0.75rem; flex-wrap:wrap; font-size:0.8rem;">
|
|
<div class="badge" style="background:var(--accent-bg); color:var(--accent);">WorkspaceConfig.swift</div>
|
|
<div class="badge" style="background:#f5f3ff; color:#7c3aed;">LMStudioMonitor.swift</div>
|
|
<div class="badge" style="background:#ecfdf5; color:#059669;">MasterDashboardView.swift</div>
|
|
<div class="badge" style="background:#ecfeff; color:#0891b2;">DataLakeMappingView.swift</div>
|
|
<div class="badge" style="background:#fffbeb; color:#d97706;">WorkspaceDetailView.swift</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 12: INSTALLATION
|
|
============================================================ -->
|
|
<div class="slide" data-slide="11">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-green" style="margin-bottom:1rem;">Installation</span>
|
|
<h2>From Clone to Running in One Command</h2>
|
|
<div class="two-col">
|
|
<div>
|
|
<h3>Prerequisites</h3>
|
|
<table class="data-table" style="margin-bottom:1.5rem;">
|
|
<tr><td><strong>macOS</strong></td><td>Tahoe (26.x) on Apple Silicon</td></tr>
|
|
<tr><td><strong>Xcode</strong></td><td>26.x or Command Line Tools</td></tr>
|
|
<tr><td><strong>Go</strong></td><td>1.22+ (from go.dev or Homebrew)</td></tr>
|
|
<tr><td><strong>LM Studio</strong></td><td>From lmstudio.ai (free)</td></tr>
|
|
</table>
|
|
<h3>What the Installer Does</h3>
|
|
<ol class="step-list">
|
|
<li>
|
|
<strong>Checks prerequisites</strong>
|
|
<p>Validates macOS version, architecture, Xcode tools, Swift, and Go version.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Builds Go daemon</strong>
|
|
<p>Compiles the Go daemon into a single 17MB binary with zero runtime dependencies.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Builds the .app bundle</strong>
|
|
<p>Swift release build → proper <code>.app</code> with Info.plist, bundled daemon, and WebGPU resources.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Installs to /Applications</strong>
|
|
<p>Copies the app bundle. Appears in Launchpad and Spotlight immediately.</p>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
<div>
|
|
<h3>One-Line Install</h3>
|
|
<div class="code-block" style="font-size:0.75rem; margin-bottom:1.25rem;">
|
|
<span class="comment"># Clone and install</span>
|
|
<span class="fn">git</span> clone <repo-url>
|
|
<span class="keyword">cd</span> LongLocalTimeHorizonInfoRetrieval
|
|
<span class="fn">bash</span> scripts/install.sh</div>
|
|
<h3>Make Targets</h3>
|
|
<div class="code-block" style="font-size:0.75rem; margin-bottom:1.25rem;">
|
|
<span class="fn">make</span> build <span class="comment"># Build .app bundle to dist/</span>
|
|
<span class="fn">make</span> install <span class="comment"># Full install to /Applications</span>
|
|
<span class="fn">make</span> test <span class="comment"># Run all tests</span>
|
|
<span class="fn">make</span> app-run <span class="comment"># Dev mode (swift run)</span>
|
|
<span class="fn">make</span> daemon-run <span class="comment"># Run daemon directly</span>
|
|
<span class="fn">make</span> clean <span class="comment"># Remove build artifacts</span></div>
|
|
<h3>App Bundle Structure</h3>
|
|
<div class="code-block" style="font-size:0.72rem;">
|
|
Knowledge Refinery.app/
|
|
Contents/
|
|
Info.plist
|
|
MacOS/
|
|
KnowledgeRefinery <span class="comment"># launcher</span>
|
|
KnowledgeRefinery-bin <span class="comment"># Swift binary</span>
|
|
Resources/
|
|
knowledge-refinery-daemon <span class="comment"># Go binary (17MB)</span>
|
|
WebGPU/ <span class="comment"># 3D renderer</span>
|
|
universe.html/js/wgsl</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 13: APP WALKTHROUGH
|
|
============================================================ -->
|
|
<div class="slide" data-slide="12">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">App Walkthrough</span>
|
|
<h2>Dashboard + Workspace: Two-Level UI</h2>
|
|
<div class="two-col">
|
|
<div>
|
|
<h3 style="color:var(--accent);">Level 1: Dashboard</h3>
|
|
<div class="card" style="margin-bottom:0.75rem;">
|
|
<div class="icon indigo">📊</div>
|
|
<h3>LM Studio Card</h3>
|
|
<p>Green/red status, loaded model names (chat + embedding), port number. Polls directly at <code>/v1/models</code>.</p>
|
|
</div>
|
|
<div class="card" style="margin-bottom:0.75rem;">
|
|
<div class="icon violet">📦</div>
|
|
<h3>Workspace Grid</h3>
|
|
<p>Color-coded cards with daemon toggle, vector count, data lake count. Click "Open" to enter a workspace.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon blue">🗺</div>
|
|
<h3>Data Lake Map</h3>
|
|
<p>Canvas-drawn Bézier curves connecting folder paths to workspaces. See which folders feed which knowledge bases.</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h3 style="color:var(--accent-2);">Level 2: Workspace</h3>
|
|
<div class="card" style="margin-bottom:0.75rem;">
|
|
<div class="icon emerald">🔎</div>
|
|
<h3>Search + Universe + Concepts</h3>
|
|
<p>Full NavigationSplitView with semantic search, WebGPU 3D concept graph, hierarchical concept browser with "Why?" explanations.</p>
|
|
</div>
|
|
<div class="card" style="margin-bottom:0.75rem;">
|
|
<div class="icon cyan">⬇</div>
|
|
<h3>Ingest + Volumes + Assets</h3>
|
|
<p>Pipeline monitoring, volume management, file inventory. Each workspace has its own daemon process and data directory.</p>
|
|
</div>
|
|
<div class="card">
|
|
<div class="icon amber">🔧</div>
|
|
<h3>Daemon Controls + Logs</h3>
|
|
<p>Play/stop/restart buttons in the header bar. Live log viewer showing last 500 lines of daemon stdout/stderr.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="margin-top:1.5rem; text-align:center;">
|
|
<div style="display:inline-flex; gap:1rem; align-items:center; padding:0.75rem 1.5rem; background:var(--bg-subtle); border-radius:var(--radius-sm); font-size:0.8rem; color:var(--fg-muted);">
|
|
<span>🟢 LM Studio (shared)</span>
|
|
<span>🟢 Workspace 1 :8742</span>
|
|
<span>🟢 Workspace 2 :8743</span>
|
|
<span style="margin-left:0.5rem;">Each workspace runs its own daemon</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 14: SEARCH DEEP DIVE
|
|
============================================================ -->
|
|
<div class="slide" data-slide="13">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">Deep Dive</span>
|
|
<h2>How Search Works</h2>
|
|
<div class="two-col">
|
|
<div>
|
|
<h3>Query Flow</h3>
|
|
<div style="background:var(--bg-subtle); border-radius:var(--radius); padding:1.25rem; font-size:0.85rem; line-height:2;">
|
|
<div style="display:flex; align-items:center; gap:0.75rem;">
|
|
<span style="background:var(--accent); color:white; width:24px; height:24px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:0.7rem; font-weight:700; flex-shrink:0;">1</span>
|
|
User types: <code>"cryptography encryption"</code>
|
|
</div>
|
|
<div style="color:var(--fg-subtle); padding-left:2.3rem;">↓</div>
|
|
<div style="display:flex; align-items:center; gap:0.75rem;">
|
|
<span style="background:#0891b2; color:white; width:24px; height:24px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:0.7rem; font-weight:700; flex-shrink:0;">2</span>
|
|
Embed query via nomic-embed-text → 768-dim vector
|
|
</div>
|
|
<div style="color:var(--fg-subtle); padding-left:2.3rem;">↓</div>
|
|
<div style="display:flex; align-items:center; gap:0.75rem;">
|
|
<span style="background:#7c3aed; color:white; width:24px; height:24px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:0.7rem; font-weight:700; flex-shrink:0;">3</span>
|
|
Brute-force cosine similarity search (in-memory vectors)
|
|
</div>
|
|
<div style="color:var(--fg-subtle); padding-left:2.3rem;">↓</div>
|
|
<div style="display:flex; align-items:center; gap:0.75rem;">
|
|
<span style="background:#059669; color:white; width:24px; height:24px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:0.7rem; font-weight:700; flex-shrink:0;">4</span>
|
|
Enrich with annotations (topics, summary, entities)
|
|
</div>
|
|
<div style="color:var(--fg-subtle); padding-left:2.3rem;">↓</div>
|
|
<div style="display:flex; align-items:center; gap:0.75rem;">
|
|
<span style="background:#d97706; color:white; width:24px; height:24px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:0.7rem; font-weight:700; flex-shrink:0;">5</span>
|
|
Return ranked results with evidence anchors
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h3>Example Result</h3>
|
|
<div class="card" style="border-left:3px solid var(--accent);">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:0.5rem;">
|
|
<strong style="font-size:0.9rem;">📄 cryptography_basics.html</strong>
|
|
<span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.7rem;">score: 0.378</span>
|
|
</div>
|
|
<div style="margin-bottom:0.75rem;">
|
|
<div style="font-size:0.75rem; font-weight:600; color:var(--fg-subtle); margin-bottom:0.2rem;">LLM SUMMARY</div>
|
|
<p style="font-size:0.82rem; color:var(--fg-muted); font-style:italic;">"This text describes various cryptographic techniques, including symmetric and asymmetric encryption..."</p>
|
|
</div>
|
|
<div style="margin-bottom:0.75rem;">
|
|
<div style="font-size:0.75rem; font-weight:600; color:var(--fg-subtle); margin-bottom:0.2rem;">ENTITIES</div>
|
|
<div style="display:flex; flex-wrap:wrap; gap:0.3rem;">
|
|
<span class="ann-tag" style="background:#eef2ff; color:#4f46e5; font-size:0.7rem;">AES</span>
|
|
<span class="ann-tag" style="background:#eef2ff; color:#4f46e5; font-size:0.7rem;">RSA</span>
|
|
<span class="ann-tag" style="background:#eef2ff; color:#4f46e5; font-size:0.7rem;">ECC</span>
|
|
<span class="ann-tag" style="background:#fffbeb; color:#d97706; font-size:0.7rem;">NIST</span>
|
|
<span class="ann-tag" style="background:#eef2ff; color:#4f46e5; font-size:0.7rem;">SHA-256</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<span class="badge" style="background:#f0f2f7; font-size:0.7rem;">⚖ neutral</span>
|
|
</div>
|
|
</div>
|
|
<div style="margin-top:1rem; font-size:0.82rem; color:var(--fg-muted);">
|
|
<strong>Key insight:</strong> A search for "cryptography" finds the right file even though the query doesn't match any specific keywords in other documents. Semantic similarity beats keyword search.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 15: API REFERENCE
|
|
============================================================ -->
|
|
<div class="slide" data-slide="14">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-cyan" style="margin-bottom:1rem;">API Reference</span>
|
|
<h2>RESTful API Endpoints</h2>
|
|
<p class="lead">The daemon exposes a clean REST API on localhost. All endpoints are accessible without authentication — the daemon only binds to 127.0.0.1.</p>
|
|
<table class="data-table" style="font-size:0.82rem;">
|
|
<thead>
|
|
<tr><th>Method</th><th>Endpoint</th><th>Description</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/health</code></td><td>Health check + LM Studio status (no auth)</td></tr>
|
|
<tr><td><span class="badge" style="background:#eff6ff; color:#2563eb; font-size:0.65rem;">POST</span></td><td><code>/volumes/add</code></td><td>Register a folder to watch</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/volumes/list</code></td><td>List all watched volumes</td></tr>
|
|
<tr><td><span class="badge" style="background:#eff6ff; color:#2563eb; font-size:0.65rem;">POST</span></td><td><code>/ingest/start</code></td><td>Trigger full 6-stage pipeline</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/ingest/status</code></td><td>Pipeline progress + per-stage counts</td></tr>
|
|
<tr><td><span class="badge" style="background:#eff6ff; color:#2563eb; font-size:0.65rem;">POST</span></td><td><code>/search</code></td><td>Semantic vector search with annotation enrichment</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/search/quick?q=...</code></td><td>Quick search via query parameter</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/universe/snapshot?lod=macro</code></td><td>Graph snapshot at given LOD level</td></tr>
|
|
<tr><td><span class="badge" style="background:#eff6ff; color:#2563eb; font-size:0.65rem;">POST</span></td><td><code>/universe/focus</code></td><td>Focus on node and return neighborhood</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/concepts/list</code></td><td>List all concept clusters</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/concepts/{id}</code></td><td>Concept detail with member chunks</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/concepts/{id}/why</code></td><td>"Why this concept?" with evidence chain</td></tr>
|
|
<tr><td><span class="badge" style="background:#ecfdf5; color:#059669; font-size:0.65rem;">GET</span></td><td><code>/evidence/chunk/{id}/annotation</code></td><td>Full annotation for a specific chunk</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 16: TECH STACK
|
|
============================================================ -->
|
|
<div class="slide" data-slide="15">
|
|
<div class="slide-inner">
|
|
<span class="tag tag-accent" style="margin-bottom:1rem;">Technology</span>
|
|
<h2>Technology Stack</h2>
|
|
<div class="two-col">
|
|
<div>
|
|
<h3>Frontend (macOS App)</h3>
|
|
<table class="data-table">
|
|
<tr><td><strong>Framework</strong></td><td>SwiftUI (macOS Tahoe / 26.x)</td></tr>
|
|
<tr><td><strong>Language</strong></td><td>Swift 6.2.3</td></tr>
|
|
<tr><td><strong>Architecture</strong></td><td>Multi-workspace + dashboard</td></tr>
|
|
<tr><td><strong>3D Rendering</strong></td><td>WebGPU via WKWebView</td></tr>
|
|
<tr><td><strong>File Preview</strong></td><td>QuickLook framework</td></tr>
|
|
<tr><td><strong>Build</strong></td><td>SPM + build.sh → .app bundle</td></tr>
|
|
<tr><td><strong>Install</strong></td><td><code>make install</code> or install.sh</td></tr>
|
|
</table>
|
|
</div>
|
|
<div>
|
|
<h3>Backend (Go Daemon)</h3>
|
|
<table class="data-table">
|
|
<tr><td><strong>Language</strong></td><td>Go 1.22+ (single 17MB binary)</td></tr>
|
|
<tr><td><strong>HTTP Router</strong></td><td>chi/v5 + CORS middleware</td></tr>
|
|
<tr><td><strong>Storage</strong></td><td>SQLite (metadata + vectors + graph, WAL mode)</td></tr>
|
|
<tr><td><strong>SQLite Driver</strong></td><td>modernc.org/sqlite (pure Go, no CGo)</td></tr>
|
|
<tr><td><strong>LLM Client</strong></td><td>net/http → LM Studio</td></tr>
|
|
<tr><td><strong>PDF</strong></td><td>pdftotext (poppler-utils)</td></tr>
|
|
<tr><td><strong>Tokenizer</strong></td><td>tiktoken-go (cl100k_base)</td></tr>
|
|
<tr><td><strong>Math</strong></td><td>Pure Go (k-means++, cosine sim)</td></tr>
|
|
</table>
|
|
<h3 style="margin-top:1.5rem;">LM Studio Models</h3>
|
|
<table class="data-table">
|
|
<tr><td><strong>Chat</strong></td><td>qwen3-4b-2507</td></tr>
|
|
<tr><td><strong>Embeddings</strong></td><td>nomic-embed-text-v1.5 (768-dim)</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
SLIDE 17: FINAL
|
|
============================================================ -->
|
|
<div class="slide title-slide" data-slide="16">
|
|
<div class="slide-inner" style="text-align:center;">
|
|
<h1 class="gradient">Your Knowledge,<br>Refined.</h1>
|
|
<p class="subtitle" style="margin:0.5rem auto 2.5rem;">
|
|
Fully local. Evidence-native. LLM-powered. Installable.<br>
|
|
Clone, install, launch — from raw documents to searchable knowledge in minutes.
|
|
</p>
|
|
<div class="stats-row" style="justify-content:center; margin-bottom:2.5rem;">
|
|
<div class="stat"><div class="stat-num">97</div><div class="stat-label">Tests Passing</div></div>
|
|
<div class="stat"><div class="stat-num">0</div><div class="stat-label">Compiler Warnings</div></div>
|
|
<div class="stat"><div class="stat-num">~90</div><div class="stat-label">Source Files</div></div>
|
|
<div class="stat"><div class="stat-num">100%</div><div class="stat-label">Local</div></div>
|
|
</div>
|
|
<div style="display:flex; gap:1rem; justify-content:center; flex-wrap:wrap; font-size:0.82rem;">
|
|
<div class="badge" style="background:var(--accent-bg); color:var(--accent);">📁 daemon-go/</div>
|
|
<div class="badge" style="background:#f5f3ff; color:#7c3aed;">📱 apps/macos/KnowledgeRefinery/</div>
|
|
<div class="badge" style="background:#ecfdf5; color:#059669;">🧪 daemon-go/internal/*/</div>
|
|
<div class="badge" style="background:#fffbeb; color:#d97706;">🛠 scripts/install.sh</div>
|
|
</div>
|
|
<div style="margin-top:1.5rem;">
|
|
<div class="code-block" style="display:inline-block; font-size:0.8rem; padding:0.6rem 1.5rem; text-align:left;">
|
|
<span class="fn">git</span> clone <repo> && <span class="keyword">cd</span> LongLocalTimeHorizonInfoRetrieval && <span class="fn">make</span> install</div>
|
|
</div>
|
|
<div style="margin-top:1.5rem; color:var(--fg-subtle); font-size:0.8rem;">
|
|
Built with SwiftUI, Go, chi, SQLite, WebGPU, and local LLMs
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /deck -->
|
|
|
|
<!-- ============================================================
|
|
NAVIGATION BAR
|
|
============================================================ -->
|
|
<div class="nav-bar">
|
|
<button class="nav-btn" id="btn-prev" onclick="navigate(-1)">← Prev</button>
|
|
<div class="progress"><div class="progress-fill" id="progress-fill"></div></div>
|
|
<span class="slide-counter" id="slide-counter">1 / 17</span>
|
|
<button class="nav-btn" id="btn-next" onclick="navigate(1)">Next →</button>
|
|
</div>
|
|
|
|
<script>
|
|
// ============================================================
|
|
// SLIDE ENGINE
|
|
// ============================================================
|
|
const slides = document.querySelectorAll('.slide');
|
|
let current = 0;
|
|
const total = slides.length;
|
|
|
|
function goTo(n) {
|
|
if (n < 0 || n >= total) return;
|
|
const prev = current;
|
|
current = n;
|
|
|
|
slides.forEach((s, i) => {
|
|
s.classList.remove('active', 'prev');
|
|
if (i === current) s.classList.add('active');
|
|
else if (i === prev) s.classList.add('prev');
|
|
});
|
|
|
|
document.getElementById('slide-counter').textContent = `${current + 1} / ${total}`;
|
|
document.getElementById('progress-fill').style.width = `${((current + 1) / total) * 100}%`;
|
|
document.getElementById('btn-prev').disabled = current === 0;
|
|
document.getElementById('btn-next').disabled = current === total - 1;
|
|
|
|
// Scroll active slide to top if overflowing
|
|
slides[current].scrollTop = 0;
|
|
}
|
|
|
|
function navigate(dir) {
|
|
goTo(current + dir);
|
|
}
|
|
|
|
// Keyboard
|
|
document.addEventListener('keydown', e => {
|
|
if (e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); navigate(1); }
|
|
else if (e.key === 'ArrowLeft') { e.preventDefault(); navigate(-1); }
|
|
else if (e.key === 'Home') { e.preventDefault(); goTo(0); }
|
|
else if (e.key === 'End') { e.preventDefault(); goTo(total - 1); }
|
|
});
|
|
|
|
// Touch/swipe
|
|
let touchStartX = 0;
|
|
let touchStartY = 0;
|
|
document.addEventListener('touchstart', e => {
|
|
touchStartX = e.touches[0].clientX;
|
|
touchStartY = e.touches[0].clientY;
|
|
}, { passive: true });
|
|
|
|
document.addEventListener('touchend', e => {
|
|
const dx = e.changedTouches[0].clientX - touchStartX;
|
|
const dy = e.changedTouches[0].clientY - touchStartY;
|
|
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 50) {
|
|
navigate(dx < 0 ? 1 : -1);
|
|
}
|
|
}, { passive: true });
|
|
|
|
// Init
|
|
goTo(0);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|