Skip to content

Commit a09d6b1

Browse files
committed
fhir gateway
1 parent d1486ab commit a09d6b1

18 files changed

Lines changed: 783 additions & 108 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules/
22
dist/
33
.astro/
4+
.DS_Store

public/favicon.svg

Lines changed: 4 additions & 0 deletions
Loading
35.7 MB
Binary file not shown.
100 KB
Loading
69.3 KB
Loading

src/components/FhirDiagram.astro

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
---
3+
4+
<div class="fhir-diagram">
5+
<svg viewBox="0 0 600 320" xmlns="http://www.w3.org/2000/svg">
6+
<!-- Paths -->
7+
<!-- Your App → REST → down → Gateway -->
8+
<path id="path-rest" d="M 135 50 L 250 50 L 250 125" fill="none" stroke="var(--muted-color)" stroke-width="1" />
9+
<!-- Gateway → Platforms -->
10+
<path id="path-platforms" d="M 355 155 L 420 155" fill="none" stroke="var(--muted-color)" stroke-width="1" />
11+
<!-- AI Agent → MCP → up → Gateway -->
12+
<path id="path-mcp" d="M 135 265 L 250 265 L 250 185" fill="none" stroke="var(--muted-color)" stroke-width="1" />
13+
<!-- User → up → Gateway/Platforms area -->
14+
<path id="path-user" d="M 500 240 L 500 215" fill="none" stroke="var(--muted-color)" stroke-width="1" />
15+
16+
<!-- Animated dots — one direction -->
17+
<circle r="3" fill="var(--link-color)">
18+
<animateMotion dur="3s" repeatCount="indefinite">
19+
<mpath href="#path-rest" />
20+
</animateMotion>
21+
<animate attributeName="opacity" values="0;1;1;0" keyTimes="0;0.05;0.9;1" dur="3s" repeatCount="indefinite" />
22+
</circle>
23+
<circle r="3" fill="var(--link-color)" opacity="0">
24+
<animateMotion dur="3s" begin="1.5s" repeatCount="indefinite">
25+
<mpath href="#path-platforms" />
26+
</animateMotion>
27+
<animate attributeName="opacity" values="0;1;1;0" keyTimes="0;0.05;0.9;1" dur="3s" begin="1.5s" repeatCount="indefinite" />
28+
</circle>
29+
<circle r="3" fill="var(--link-color)" opacity="0">
30+
<animateMotion dur="3s" begin="0.75s" repeatCount="indefinite">
31+
<mpath href="#path-mcp" />
32+
</animateMotion>
33+
<animate attributeName="opacity" values="0;1;1;0" keyTimes="0;0.05;0.9;1" dur="3s" begin="0.75s" repeatCount="indefinite" />
34+
</circle>
35+
<circle r="3" fill="var(--link-color)" opacity="0">
36+
<animateMotion dur="3s" begin="2.25s" repeatCount="indefinite">
37+
<mpath href="#path-user" />
38+
</animateMotion>
39+
<animate attributeName="opacity" values="0;1;1;0" keyTimes="0;0.05;0.9;1" dur="3s" begin="2.25s" repeatCount="indefinite" />
40+
</circle>
41+
42+
<!-- Labels on paths -->
43+
<text x="175" y="42" class="diagram-text label-text">REST</text>
44+
<text x="175" y="257" class="diagram-text label-text">MCP</text>
45+
46+
<!-- Node: Your App -->
47+
<rect x="20" y="25" width="115" height="50" rx="4" class="diagram-box" />
48+
<text x="77" y="53" class="diagram-text node-text">Your App</text>
49+
50+
<!-- Node: FHIR Gateway -->
51+
<rect x="185" y="125" width="170" height="60" rx="4" class="diagram-box gateway-box" />
52+
<text x="270" y="158" class="diagram-text gateway-text">FHIR Gateway</text>
53+
54+
<!-- Node: Platforms -->
55+
<rect x="420" y="95" width="160" height="120" rx="4" class="diagram-box platform-box" />
56+
<text x="500" y="122" class="diagram-text platform-text">Aetna, Cigna,</text>
57+
<text x="500" y="142" class="diagram-text platform-text">Epic, Cerner,</text>
58+
<text x="500" y="162" class="diagram-text platform-text">UHC, Humana,</text>
59+
<text x="500" y="186" class="diagram-text platform-accent">60+ systems</text>
60+
61+
<!-- Node: AI Agent -->
62+
<rect x="20" y="240" width="115" height="50" rx="4" class="diagram-box" />
63+
<text x="77" y="268" class="diagram-text node-text">AI Agent</text>
64+
65+
<!-- Node: User -->
66+
<rect x="430" y="240" width="140" height="68" rx="4" class="diagram-box" />
67+
<text x="500" y="260" class="diagram-text node-text">User</text>
68+
<text x="500" y="278" class="diagram-text sub-text">(authorizes</text>
69+
<text x="500" y="292" class="diagram-text sub-text">via OAuth)</text>
70+
</svg>
71+
</div>
72+
73+
<style>
74+
.fhir-diagram {
75+
margin: 2rem 0;
76+
padding: 1rem;
77+
border: 1px solid var(--border-color);
78+
border-radius: 8px;
79+
overflow-x: auto;
80+
}
81+
82+
.fhir-diagram svg {
83+
display: block;
84+
width: 100%;
85+
max-width: 600px;
86+
margin: 0 auto;
87+
height: auto;
88+
}
89+
90+
.diagram-text {
91+
font-family: "JetBrains Mono", "Fira Code", "SF Mono", Menlo, monospace;
92+
text-anchor: middle;
93+
dominant-baseline: middle;
94+
}
95+
96+
.node-text {
97+
font-size: 13px;
98+
font-weight: 600;
99+
fill: var(--heading-color);
100+
}
101+
102+
.gateway-text {
103+
font-size: 14px;
104+
font-weight: 600;
105+
fill: var(--heading-color);
106+
}
107+
108+
.sub-text {
109+
font-size: 11px;
110+
fill: var(--muted-color);
111+
}
112+
113+
.label-text {
114+
font-size: 11px;
115+
fill: var(--muted-color);
116+
}
117+
118+
.platform-text {
119+
font-size: 11px;
120+
fill: var(--muted-color);
121+
}
122+
123+
.platform-accent {
124+
font-size: 11px;
125+
font-weight: 500;
126+
fill: var(--link-color);
127+
}
128+
129+
.diagram-box {
130+
fill: none;
131+
stroke: var(--muted-color);
132+
stroke-width: 1.5;
133+
}
134+
135+
.gateway-box {
136+
stroke: var(--link-color);
137+
stroke-width: 2;
138+
}
139+
140+
.platform-box {
141+
stroke-dasharray: 5 3;
142+
}
143+
</style>

src/components/Nav.astro

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,6 @@
2626
return localStorage.getItem("theme") || "system";
2727
}
2828

29-
function applyTheme(setting) {
30-
if (setting === "system") {
31-
const sys = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
32-
document.documentElement.setAttribute("data-theme", sys);
33-
} else {
34-
document.documentElement.setAttribute("data-theme", setting);
35-
}
36-
document.documentElement.setAttribute("data-theme-setting", setting);
37-
}
38-
3929
function cycleTheme() {
4030
const current = getThemeSetting();
4131
const next = current === "dark" ? "light" : current === "light" ? "system" : "dark";
@@ -44,14 +34,14 @@
4434
} else {
4535
localStorage.setItem("theme", next);
4636
}
47-
applyTheme(next);
37+
// Reuse the global applyThemeToDoc from BaseLayout
38+
applyThemeToDoc(document);
4839
}
4940

50-
applyTheme(getThemeSetting());
5141
btn?.addEventListener("click", cycleTheme);
5242

5343
window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", () => {
54-
if (getThemeSetting() === "system") applyTheme("system");
44+
if (getThemeSetting() === "system") applyThemeToDoc(document);
5545
});
5646
});
5747
</script>

src/components/PostList.astro

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
interface Post {
3+
id: string;
4+
data: {
5+
title: string;
6+
description: string;
7+
pubDate: Date;
8+
};
9+
}
10+
11+
interface Props {
12+
posts: Post[];
13+
}
14+
15+
const { posts } = Astro.props;
16+
---
17+
18+
<ul class="post-list">
19+
{
20+
posts.map((post) => (
21+
<li>
22+
<a href={`/writing/${post.id}/`}>
23+
<div class="post-header">
24+
<span>{post.data.title}</span>
25+
<time datetime={post.data.pubDate.toISOString()}>
26+
{post.data.pubDate.toLocaleDateString("en-US", {
27+
year: "numeric",
28+
month: "short",
29+
day: "numeric",
30+
})}
31+
</time>
32+
</div>
33+
<p class="post-description">{post.data.description}</p>
34+
</a>
35+
</li>
36+
))
37+
}
38+
</ul>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
interface Props {
3+
headings: { depth: number; slug: string; text: string }[];
4+
title: string;
5+
}
6+
7+
const { headings, title } = Astro.props;
8+
const filtered = headings.filter((h) => h.depth === 2 || h.depth === 3);
9+
---
10+
11+
{filtered.length > 0 && (
12+
<nav class="toc">
13+
<details class="toc-mobile">
14+
<summary>Table of Contents</summary>
15+
<ul>
16+
<li><a href="#" class="toc-top">{title}</a></li>
17+
{filtered.map((h) => (
18+
<li class={h.depth === 3 ? "toc-indent" : ""}>
19+
<a href={`#${h.slug}`}>{h.text}</a>
20+
</li>
21+
))}
22+
</ul>
23+
</details>
24+
<div class="toc-desktop">
25+
<span class="toc-title">Contents</span>
26+
<ul>
27+
<li><a href="#" class="toc-top">{title}</a></li>
28+
{filtered.map((h) => (
29+
<li class={h.depth === 3 ? "toc-indent" : ""}>
30+
<a href={`#${h.slug}`}>{h.text}</a>
31+
</li>
32+
))}
33+
</ul>
34+
</div>
35+
</nav>
36+
)}
37+
38+
<script>
39+
function initToc() {
40+
const tocLinks = document.querySelectorAll(".toc a:not(.toc-top)");
41+
const topLinks = document.querySelectorAll(".toc a.toc-top");
42+
if (!tocLinks.length) return;
43+
44+
const headingEls = Array.from(tocLinks).map((link) =>
45+
document.getElementById(link.getAttribute("href")!.slice(1))
46+
).filter(Boolean) as HTMLElement[];
47+
48+
if (!headingEls.length) return;
49+
50+
function updateActive() {
51+
let current: HTMLElement | null = null;
52+
for (const el of headingEls) {
53+
if (el.getBoundingClientRect().top <= 100) {
54+
current = el;
55+
} else {
56+
break;
57+
}
58+
}
59+
tocLinks.forEach((link) => link.classList.remove("toc-active"));
60+
topLinks.forEach((link) => link.classList.remove("toc-active"));
61+
if (current) {
62+
document
63+
.querySelectorAll(`.toc a[href="#${current.id}"]`)
64+
.forEach((link) => link.classList.add("toc-active"));
65+
} else {
66+
topLinks.forEach((link) => link.classList.add("toc-active"));
67+
}
68+
}
69+
70+
topLinks.forEach((link) => {
71+
link.addEventListener("click", (e) => {
72+
e.preventDefault();
73+
window.scrollTo({ top: 0, behavior: "smooth" });
74+
history.replaceState(null, "", window.location.pathname);
75+
});
76+
});
77+
78+
updateActive();
79+
window.addEventListener("scroll", updateActive, { passive: true });
80+
81+
return () => window.removeEventListener("scroll", updateActive);
82+
}
83+
84+
let cleanup = initToc();
85+
document.addEventListener("astro:page-load", () => {
86+
cleanup?.();
87+
cleanup = initToc();
88+
});
89+
</script>

src/components/ThemeToggle.astro

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)