Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions services/web-dashboard/public/widget/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# `<ufo-intel-widget>` — embeddable UFO/UAP intelligence widget

A self-contained, zero-backend Web Component that turns a JSON or RSS feed of
declassified UFO / UAP releases into an analyst-grade live intelligence
console, complete with a BYO-key agentic assistant.

> Live demo: <https://dascient.github.io/UACP/widget/>

## Embed

```html
<script type="module"
src="https://dascient.github.io/UACP/widget/ufo-intel-widget.js"></script>

<ufo-intel-widget
data-source="https://your-mirror.example/ufo/index.json"
refresh-interval="30000"
theme="dark"
llm-provider="openai"
llm-model="gpt-4o-mini"></ufo-intel-widget>
```

## Attributes

| Attribute | Default | Notes |
| ------------------ | -------------------------- | ------------------------------------------------------------------ |
| `data-source` | demo placeholder | One or more whitespace-separated URLs (JSON, RSS, Atom). |
| `refresh-interval` | `30000` | Milliseconds between polls (minimum 5 000). |
| `theme` | `dark` | `dark`, `light`, or `compact`. |
| `llm-provider` | `openai` | `openai`, `anthropic`, or `local` (OpenAI-compatible). |
| `llm-model` | `""` | Model identifier passed to the provider. |
| `api-key` | `""` (use Settings dialog) | Optional inline key — prefer the in-widget Settings panel instead. |

## Features

- **Live intelligence feed** merging file releases, analyst notes (added
in-widget), and system alerts (new-tranche detection, source-down).
- **Deep analytics tabs**: hand-rolled SVG timeline, agency × keyword
network graph, file-type composition bars, and release-cadence histogram.
All update live and respect the active filter.
- **Agentic assistant** (slide-out panel) with BYO-key support for OpenAI,
Anthropic, and any OpenAI-compatible local endpoint (Ollama, LM Studio,
llama.cpp). Streams responses where the provider supports SSE.
- **Client-side RAG**: TF-IDF retrieval over the loaded dataset (no
external embeddings model required). Slash commands: `/summarize`,
`/compare`, `/report`, `/risk <term>`, `/timeline`, `/find <term>`.
- **Offline resilience**: IndexedDB cache, stale banner with last-fetch
timestamp.
- **Security**: No telemetry, no external CDN dependencies. API keys live
only in `localStorage` on the user's device.
- **Accessibility**: Shadow-DOM-isolated styles, ARIA roles, keyboard nav
(`R` refresh, `/` open assistant, `Esc` close modal), `prefers-reduced-motion`.
- **Audit log**: Local-only record of every fetch, query, and user
action — viewable in the Audit tab, never exfiltrated.
- **Export**: CSV/JSON of the filtered dataset and Markdown chat transcripts.

## Data schema

Each release record is normalised to:

```ts
{ id, title, agency, url, blurb, releaseDate, type }
```

The loader accepts any of:

- `{ pdfs: [...], images: [...], videos: [...] }` (the war.gov-mirror shape),
- `{ items: [...] }`,
- a top-level array `[ … ]`, or
- RSS/Atom XML (auto-detected from `Content-Type` or file extension).

## Privacy & security

- Everything (data fetching, embeddings, retrieval) runs in the user's
browser. The only outbound network traffic is (a) the configured
data source URL(s), and (b) the configured LLM provider URL when the
user submits a chat message.
- The widget never reads or writes cookies. Styles are isolated in
Shadow DOM so it cannot collide with host CSS.

## Development

The widget is a single dependency-free ES module at
[`ufo-intel-widget.js`](./ufo-intel-widget.js). Open
[`index.html`](./index.html) directly in any modern browser — no build
step required.

Deployed to GitHub Pages via the repository's existing
[`.github/workflows/static_deploy.yml`](../../../../.github/workflows/static_deploy.yml)
which copies `services/web-dashboard/public/` into the published site root.
187 changes: 187 additions & 0 deletions services/web-dashboard/public/widget/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>UFO·INTEL — Plug-and-play intelligence widget</title>
<meta name="description" content="Self-contained, client-side web component for live UFO/UAP release intelligence with BYO-key agentic assistant.">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='%235fd0c4'/%3E%3C/svg%3E">
<style>
:root {
color-scheme: dark;
--bg: #050709;
--panel: #0a0d12;
--fg: #d6e0ee;
--fg-dim: #8593a8;
--accent: #5fd0c4;
--border: #1f2a3a;
}
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg);
font-family: ui-monospace, "JetBrains Mono", "SF Mono", Consolas, Menlo, monospace; }
body { padding: 28px 32px 60px; max-width: 1280px; margin: 0 auto; }
h1 { letter-spacing: 0.15em; text-transform: uppercase; font-size: 18px;
border-bottom: 1px solid var(--border); padding-bottom: 10px; }
h1 .dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%;
background: var(--accent); box-shadow: 0 0 8px var(--accent); margin-right: 10px; }
p { color: var(--fg-dim); line-height: 1.55; }
code, pre {
background: var(--panel); border: 1px solid var(--border);
border-radius: 3px; padding: 1px 5px;
}
pre { padding: 10px 12px; overflow-x: auto; }
a { color: var(--accent); }
.grid { display: grid; gap: 18px; grid-template-columns: 1fr; }
.note { font-size: 12px; color: var(--fg-dim); }
section { margin-top: 28px; }
label { display: block; margin: 10px 0 4px; font-size: 11px;
color: var(--fg-dim); letter-spacing: 0.1em; text-transform: uppercase; }
input[type="url"], input[type="number"], select {
width: 100%; max-width: 520px;
background: var(--panel); color: var(--fg); border: 1px solid var(--border);
border-radius: 3px; padding: 6px 8px; font: inherit;
}
.demo-bar {
display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0;
}
button {
font: inherit; color: inherit; background: var(--panel);
border: 1px solid var(--border); border-radius: 3px; padding: 6px 12px;
cursor: pointer;
}
button:hover { border-color: var(--accent); }
</style>
</head>
<body>
<h1><span class="dot"></span>UFO·INTEL — embeddable intelligence widget</h1>
<p>
A zero-backend, single-file <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components">Web Component</a>
that any analyst can drop into an existing intranet portal. Live feed,
deep-analytics dashboard, and BYO-key agentic assistant — all running
entirely in the browser.
</p>

<section>
<h2 style="font-size:12px;letter-spacing:0.12em;text-transform:uppercase;color:var(--fg-dim);">Live demo</h2>
<div class="demo-bar">
<label style="margin:0;align-self:center;">Source</label>
<input id="src" type="url" placeholder="https://…/index.json or RSS"
value="">
<label style="margin:0;align-self:center;">Refresh (ms)</label>
<input id="ref" type="number" min="5000" step="1000" value="30000" style="max-width:140px;">
<button id="apply">Apply</button>
<button id="sample">Load sample dataset</button>
</div>
<p class="note">
No data source is reachable in the public demo by default — paste your
own JSON/RSS endpoint, or click <em>Load sample dataset</em> to use a
bundled demonstration fixture so you can exercise every feature offline.
</p>

<ufo-intel-widget
id="widget"
refresh-interval="30000"
theme="dark"
llm-provider="openai"
llm-model="gpt-4o-mini"
style="display:block;margin-top:18px;"></ufo-intel-widget>
</section>

<section>
<h2 style="font-size:12px;letter-spacing:0.12em;text-transform:uppercase;color:var(--fg-dim);">Embed snippet</h2>
<p class="note">Replace the script URL below with the deployment URL of your fork
(or a relative path if you self-host alongside your portal).</p>
<pre>&lt;script type="module" src="https://dascient.github.io/UACP/widget/ufo-intel-widget.js"&gt;&lt;/script&gt;

&lt;ufo-intel-widget
data-source="https://your-mirror.example/ufo/index.json"
refresh-interval="30000"
theme="dark"
llm-provider="openai"
llm-model="gpt-4o-mini"&gt;&lt;/ufo-intel-widget&gt;</pre>

<h3 style="font-size:11px;letter-spacing:0.12em;text-transform:uppercase;color:var(--fg-dim);">Attributes</h3>
<ul class="note">
<li><code>data-source</code> — one or more whitespace-separated URLs (JSON, RSS, or Atom).</li>
<li><code>refresh-interval</code> — milliseconds between fetches (minimum 5000).</li>
<li><code>theme</code> — <code>dark</code> (default), <code>light</code>, or <code>compact</code>.</li>
<li><code>llm-provider</code> — <code>openai</code>, <code>anthropic</code>, or <code>local</code> (OpenAI-compatible).</li>
<li><code>llm-model</code> — model identifier passed to the provider.</li>
<li><code>api-key</code> — optional inline key (prefer <em>Settings → LLM provider</em> instead).</li>
</ul>

<h3 style="font-size:11px;letter-spacing:0.12em;text-transform:uppercase;color:var(--fg-dim);">Privacy</h3>
<ul class="note">
<li>No telemetry. No external CDN required.</li>
<li>API key stored only in <code>localStorage</code> on this device.</li>
<li>The dataset is fetched directly from the source URL you configure and cached in IndexedDB.</li>
<li>RAG retrieval (TF-IDF) runs in the browser; only the top-K snippets are sent to the LLM.</li>
<li>An audit log of every refresh, query, and user action is kept locally and exportable.</li>
</ul>
</section>

<script type="module" src="./ufo-intel-widget.js"></script>
<script>
// ── Demo wiring ─────────────────────────────────────────────────────
const widget = document.getElementById('widget');
const srcInp = document.getElementById('src');
const refInp = document.getElementById('ref');

document.getElementById('apply').addEventListener('click', () => {
if (srcInp.value.trim()) widget.setAttribute('data-source', srcInp.value.trim());
if (refInp.value.trim()) widget.setAttribute('refresh-interval', refInp.value.trim());
});

// Bundled offline fixture — sourced from public DoD/Navy declassification
// narratives so analysts can exercise every feature without network access.
const SAMPLE = {
pdfs: [
{ title: 'Preliminary Assessment: Unidentified Aerial Phenomena',
agency: 'ODNI', url: 'https://www.dni.gov/files/ODNI/documents/assessments/Prelimary-Assessment-UAP-20210625.pdf',
blurb: 'Office of the Director of National Intelligence 9-page preliminary assessment delivered to Congress, June 25 2021. Reviews 144 UAP reports from US government sources, 2004-2021.',
releaseDate: '2021-06-25' },
{ title: '2022 Annual Report on Unidentified Aerial Phenomena',
agency: 'ODNI', url: 'https://www.dni.gov/files/ODNI/documents/assessments/Unclassified-2022-Annual-Report-UAP.pdf',
blurb: 'Annual report describing 510 UAP reports including 247 new ones. Highlights characterisation, transmedium concerns, and observation collection biases.',
releaseDate: '2023-01-12' },
{ title: 'AARO Historical Record Report Volume I',
agency: 'AARO', url: 'https://www.aaro.mil/Portals/136/PDFs/HRRV1-08-Mar-2024-FINAL.pdf',
blurb: 'All-domain Anomaly Resolution Office historical review of US government involvement with UAP from 1945 to 2023. Volume I of two.',
releaseDate: '2024-03-08' },
],
images: [
{ title: 'GIMBAL — gun-camera still', agency: 'USN',
url: 'https://media.defense.gov/2020/Apr/27/2002288023/-1/-1/0/GIMBAL.MP4',
blurb: 'Forward-looking infrared image from F/A-18 sensor, Atlantic training range, 2015. Anomalous rotation observed.',
releaseDate: '2020-04-27' },
{ title: 'GOFAST — sensor still', agency: 'USN',
url: 'https://media.defense.gov/2020/Apr/27/2002288024/-1/-1/0/GOFAST.MP4',
blurb: 'Low-altitude object tracked by FLIR pod, 2015. Apparent high transverse speed.',
releaseDate: '2020-04-27' },
],
videos: [
{ title: 'FLIR1 (Nimitz Tic-Tac)', agency: 'USN',
url: 'https://media.defense.gov/2020/Apr/27/2002288022/-1/-1/0/FLIR.MP4',
blurb: 'Released officially in 2020 by DOD; original 2004 USS Nimitz encounter.',
releaseDate: '2020-04-27' },
{ title: 'AARO Case Resolution: Eglin AFB sphere', agency: 'AARO',
url: 'https://www.aaro.mil/Cases/',
blurb: 'Resolved as a balloon following photogrammetric review of multi-sensor track.',
releaseDate: '2024-09-15' },
],
};

document.getElementById('sample').addEventListener('click', async () => {
const blob = new Blob([JSON.stringify(SAMPLE)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
widget.setAttribute('data-source', url);
srcInp.value = '(bundled sample)';
});

// Auto-load sample on first visit so the demo isn't blank.
if (!localStorage.getItem('ufo-intel-widget:sources') &&
!widget.getAttribute('data-source')) {
setTimeout(() => document.getElementById('sample').click(), 200);
}
</script>
</body>
</html>
Loading
Loading