-
-
-
-
-
-
- Context
-
-
- Capabilities
-
-
- Container
-
- Debug App
+
+
+
+
+
+
+ Event Log
+
- Styles Sample
- + + + + +
+ Event Log
+
+
+
-
- Callback Status - -
-
-
-
-
-
- | Callback | -Registered | -Count | -Last Payload | -
|---|
- Actions - -
-
-
-
-
Messages
-
-
-
-
-
-
+
+
+
-
-
+ Host Info + +
+
+
+
+
+
+ Context
+
+
+ Capabilities
+
+
+ Container
+
+
Styles Sample
+ +
-
+
-
-
Logging
-
-
-
-
-
-
-
-
-
+
+ + Callback Status + +
+
+
+
+
+ | Callback | +Registered | +Count | +Last Payload | +
|---|
-
Model Context
-
-
-
-
-
-
-
+
+
+
+ Actions + +
+
+
+
-
-
+
- Messages
+
+
+
+
+
+
+
-
Display Mode
-
-
-
-
+
+
-
-
+
- Logging
+
+
+
+
+
+
+
+
+
-
Links
-
-
-
+
+
-
-
+
- Model Context
+
+
+
+
+
+
+
+
-
Size
-
-
-
-
-
-
-
-
-
- Current:
-
- measuring...
+
+
+
- Display Mode
+
+
+
+
+
-
Server Tools
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ Get Time App
-
-
-
-
-
-
+
+
+
+
+ Get Time App
+
+
+
+
+
+
diff --git a/examples/integration-server/src/global.css b/examples/integration-server/src/global.css
index 801291f46..54a986089 100644
--- a/examples/integration-server/src/global.css
+++ b/examples/integration-server/src/global.css
@@ -14,8 +14,12 @@
--color-ring-primary: light-dark(#3b82f6, #60a5fa);
--border-radius-md: 6px;
--border-width-regular: 1px;
- --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
+ --font-sans:
+ ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
+ "Segoe UI Symbol", "Noto Color Emoji";
+ --font-mono:
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
+ "Courier New", monospace;
--font-weight-normal: 400;
--font-weight-bold: 700;
--font-text-md-size: 1rem;
@@ -49,7 +53,8 @@
box-sizing: border-box;
}
-html, body {
+html,
+body {
font-family: var(--font-sans);
font-size: var(--font-text-md-size);
font-weight: var(--font-weight-normal);
@@ -82,11 +87,14 @@ h6 {
line-height: var(--font-heading-sm-line-height);
}
-code, pre, kbd {
+code,
+pre,
+kbd {
font-family: var(--font-mono);
font-size: 1em;
}
-b, strong {
+b,
+strong {
font-weight: var(--font-weight-bold);
}
diff --git a/examples/integration-server/src/mcp-app.module.css b/examples/integration-server/src/mcp-app.module.css
index ae08a8ccf..6b104b452 100644
--- a/examples/integration-server/src/mcp-app.module.css
+++ b/examples/integration-server/src/mcp-app.module.css
@@ -49,11 +49,16 @@
cursor: pointer;
&:hover {
- background-color: color-mix(in srgb, var(--color-accent) 85%, var(--color-background-inverse));
+ background-color: color-mix(
+ in srgb,
+ var(--color-accent) 85%,
+ var(--color-background-inverse)
+ );
}
&:focus-visible {
- outline: calc(var(--border-width-regular) * 2) solid var(--color-ring-primary);
+ outline: calc(var(--border-width-regular) * 2) solid
+ var(--color-ring-primary);
outline-offset: var(--border-width-regular);
}
}
diff --git a/examples/map-server/mcp-app.html b/examples/map-server/mcp-app.html
index 473eb50f6..34190baae 100644
--- a/examples/map-server/mcp-app.html
+++ b/examples/map-server/mcp-app.html
@@ -1,72 +1,82 @@
-
+
-
-
-
+
+
+
CesiumJS Globe
-
-
+
+
CesiumJS Standalone Test
-
+
-
-
+
+
+
+
diff --git a/examples/pdf-server/src/global.css b/examples/pdf-server/src/global.css
index 12d392e93..babd0f841 100644
--- a/examples/pdf-server/src/global.css
+++ b/examples/pdf-server/src/global.css
@@ -2,8 +2,12 @@
box-sizing: border-box;
}
-html, body {
- font-family: system-ui, -apple-system, sans-serif;
+html,
+body {
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
font-size: 1rem;
margin: 0;
padding: 0;
diff --git a/examples/pdf-server/src/mcp-app.css b/examples/pdf-server/src/mcp-app.css
index 7600be515..286dc6141 100644
--- a/examples/pdf-server/src/mcp-app.css
+++ b/examples/pdf-server/src/mcp-app.css
@@ -13,7 +13,10 @@
--text200: light-dark(#999999, #888888);
/* Shadows */
- --shadow-page: light-dark(0 2px 8px rgba(0, 0, 0, 0.15), 0 2px 8px rgba(0, 0, 0, 0.4));
+ --shadow-page: light-dark(
+ 0 2px 8px rgba(0, 0, 0, 0.15),
+ 0 2px 8px rgba(0, 0, 0, 0.4)
+ );
--selection-bg: light-dark(rgba(0, 0, 255, 0.3), rgba(100, 150, 255, 0.4));
}
@@ -114,6 +117,7 @@ body {
flex-direction: column;
flex: 1;
overflow: visible;
+ position: relative;
}
/* Toolbar */
@@ -256,7 +260,10 @@ body {
display: block;
}
-/* Text Layer for Selection */
+/* Text Layer for Selection
+ * Critical: must include font-size and transform rules that PDF.js TextLayer
+ * relies on via CSS custom properties (--font-height, --scale-x, --rotate).
+ * Without these, text layer spans won't align with the canvas rendering. */
.text-layer {
position: absolute;
left: 0;
@@ -270,10 +277,11 @@ body {
forced-color-adjust: none;
transform-origin: 0 0;
z-index: 2;
+ /* PDF.js TextLayer sets --min-font-size on container */
+ --min-font-size-inv: calc(1 / var(--min-font-size, 1));
}
-.text-layer span,
-.text-layer br {
+.text-layer :is(span, br) {
color: transparent;
position: absolute;
white-space: pre;
@@ -281,6 +289,23 @@ body {
transform-origin: 0% 0%;
}
+/* PDF.js sets --font-height, --scale-x, --rotate as inline styles on each span.
+ * These rules apply proper font size and transforms to match the canvas. */
+.text-layer > :not(.markedContent),
+.text-layer .markedContent span:not(.markedContent) {
+ z-index: 1;
+ --font-height: 0;
+ font-size: calc(var(--scale-factor, 1) * var(--font-height));
+ --scale-x: 1;
+ --rotate: 0deg;
+ transform: rotate(var(--rotate)) scaleX(var(--scale-x))
+ scale(var(--min-font-size-inv, 1));
+}
+
+.text-layer .markedContent {
+ display: contents;
+}
+
.text-layer ::selection {
background: var(--selection-bg);
}
@@ -310,3 +335,127 @@ body {
min-height: 0; /* Allow flex item to shrink below content size */
overflow: auto; /* Scroll within the document area only */
}
+
+/* Search Button */
+.search-btn,
+.nav-btn,
+.zoom-btn,
+.fullscreen-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border: 1px solid var(--bg200);
+ border-radius: 4px;
+ background: var(--bg000);
+ color: var(--text000);
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.15s ease;
+}
+
+.search-btn:hover,
+.nav-btn:hover:not(:disabled),
+.zoom-btn:hover:not(:disabled),
+.fullscreen-btn:hover {
+ background: var(--bg100);
+ border-color: var(--bg300);
+}
+
+/* Search Bar */
+.search-bar {
+ display: flex;
+ align-items: center;
+ position: absolute;
+ top: 47px; /* below toolbar (48px - 1px border overlap) */
+ right: -1px; /* align with outer border */
+ padding: 0.375rem 0.5rem;
+ background: var(--bg000);
+ border: 1px solid var(--bg200);
+ border-top: none;
+ border-radius: 0 0 6px 6px;
+ gap: 0.5rem;
+ z-index: 10;
+}
+
+.search-input {
+ width: 200px;
+ padding: 0.25rem 0.5rem;
+ border: 1px solid var(--bg200);
+ border-radius: 4px;
+ font-size: 0.85rem;
+ background: var(--bg000);
+ color: var(--text000);
+}
+
+.search-input:focus {
+ outline: none;
+ border-color: var(--text100);
+}
+
+.search-match-count {
+ font-size: 0.8rem;
+ color: var(--text100);
+ white-space: nowrap;
+ min-width: 60px;
+}
+
+.search-nav-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
+ border: 1px solid var(--bg200);
+ border-radius: 4px;
+ background: var(--bg000);
+ color: var(--text000);
+ cursor: pointer;
+ font-size: 0.8rem;
+ transition: all 0.15s ease;
+}
+
+.search-nav-btn:hover:not(:disabled) {
+ background: var(--bg100);
+ border-color: var(--bg300);
+}
+
+.search-nav-btn:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+/* Highlight Layer */
+.highlight-layer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ pointer-events: none;
+ z-index: 1;
+}
+
+.search-highlight {
+ background: rgba(255, 255, 0, 0.4);
+ mix-blend-mode: multiply;
+ border-radius: 2px;
+ pointer-events: none;
+}
+
+.search-highlight.current {
+ background: rgba(255, 165, 0, 0.6);
+}
+
+@media (prefers-color-scheme: dark) {
+ .search-highlight {
+ background: rgba(255, 255, 0, 0.3);
+ mix-blend-mode: screen;
+ }
+
+ .search-highlight.current {
+ background: rgba(255, 165, 0, 0.5);
+ mix-blend-mode: screen;
+ }
+}
diff --git a/examples/pdf-server/src/mcp-app.ts b/examples/pdf-server/src/mcp-app.ts
index ad2b8bf0f..e5299a55a 100644
--- a/examples/pdf-server/src/mcp-app.ts
+++ b/examples/pdf-server/src/mcp-app.ts
@@ -69,6 +69,38 @@ const fullscreenBtn = document.getElementById(
const progressContainerEl = document.getElementById("progress-container")!;
const progressBarEl = document.getElementById("progress-bar")!;
const progressTextEl = document.getElementById("progress-text")!;
+const searchBtn = document.getElementById("search-btn") as HTMLButtonElement;
+searchBtn.innerHTML = ``;
+const searchBarEl = document.getElementById("search-bar")!;
+const searchInputEl = document.getElementById(
+ "search-input",
+) as HTMLInputElement;
+const searchMatchCountEl = document.getElementById("search-match-count")!;
+const searchPrevBtn = document.getElementById(
+ "search-prev-btn",
+) as HTMLButtonElement;
+const searchNextBtn = document.getElementById(
+ "search-next-btn",
+) as HTMLButtonElement;
+const searchCloseBtn = document.getElementById(
+ "search-close-btn",
+) as HTMLButtonElement;
+const highlightLayerEl = document.getElementById("highlight-layer")!;
+
+// Search state
+interface SearchMatch {
+ pageNum: number;
+ index: number;
+ length: number;
+}
+
+let searchOpen = false;
+let searchQuery = "";
+let searchDebounceTimer: ReturnType | null = null;
+const pageTextCache = new Map();
+const pageTextItemsCache = new Map();
+let allMatches: SearchMatch[] = [];
+let currentMatchIndex = -1;
// Track current display mode
let currentDisplayMode: "inline" | "fullscreen" = "inline";
@@ -106,16 +138,262 @@ function requestFitToContent() {
const paddingBottom = parseFloat(containerStyle.paddingBottom);
// Calculate required height:
- // toolbar + padding-top + page-wrapper height + padding-bottom + buffer
+ // toolbar + search-bar + padding-top + page-wrapper height + padding-bottom + buffer
const toolbarHeight = toolbarEl.offsetHeight;
+ const searchBarHeight = searchOpen ? searchBarEl.offsetHeight : 0;
const pageWrapperHeight = pageWrapperEl.offsetHeight;
const BUFFER = 10; // Buffer for sub-pixel rounding and browser quirks
const totalHeight =
- toolbarHeight + paddingTop + pageWrapperHeight + paddingBottom + BUFFER;
+ toolbarHeight +
+ searchBarHeight +
+ paddingTop +
+ pageWrapperHeight +
+ paddingBottom +
+ BUFFER;
app.sendSizeChanged({ height: totalHeight });
}
+// --- Search Functions ---
+
+async function extractAllPageText() {
+ if (!pdfDocument) return;
+ for (let i = 1; i <= totalPages; i++) {
+ if (pageTextCache.has(i)) continue;
+ try {
+ const page = await pdfDocument.getPage(i);
+ const textContent = await page.getTextContent();
+ const items = (textContent.items as Array<{ str?: string }>).map(
+ (item) => item.str || "",
+ );
+ pageTextItemsCache.set(i, items);
+ pageTextCache.set(i, items.join(""));
+ } catch (err) {
+ log.error("Error extracting text for page", i, err);
+ }
+ }
+}
+
+function performSearch(query: string) {
+ allMatches = [];
+ currentMatchIndex = -1;
+ searchQuery = query;
+
+ if (!query) {
+ updateSearchUI();
+ clearHighlights();
+ return;
+ }
+
+ const lowerQuery = query.toLowerCase();
+ for (let pageNum = 1; pageNum <= totalPages; pageNum++) {
+ const pageText = pageTextCache.get(pageNum);
+ if (!pageText) continue;
+ const lowerText = pageText.toLowerCase();
+ let startIdx = 0;
+ while (true) {
+ const idx = lowerText.indexOf(lowerQuery, startIdx);
+ if (idx === -1) break;
+ allMatches.push({ pageNum, index: idx, length: query.length });
+ startIdx = idx + 1;
+ }
+ }
+
+ // Set current match to first match on or after current page
+ if (allMatches.length > 0) {
+ const idx = allMatches.findIndex((m) => m.pageNum >= currentPage);
+ currentMatchIndex = idx >= 0 ? idx : 0;
+ }
+
+ updateSearchUI();
+ renderHighlights();
+
+ // Navigate to match page if needed
+ if (allMatches.length > 0 && currentMatchIndex >= 0) {
+ const match = allMatches[currentMatchIndex];
+ if (match.pageNum !== currentPage) {
+ goToPage(match.pageNum);
+ }
+ }
+}
+
+function renderHighlights() {
+ clearHighlights();
+ if (!searchQuery || allMatches.length === 0) return;
+
+ const spans = Array.from(
+ textLayerEl.querySelectorAll("span"),
+ ) as HTMLElement[];
+ if (spans.length === 0) return;
+
+ const pageMatches = allMatches.filter((m) => m.pageNum === currentPage);
+ if (pageMatches.length === 0) return;
+
+ const lowerQuery = searchQuery.toLowerCase();
+ const lowerQueryLen = lowerQuery.length;
+
+ // Position highlight divs over matching text using Range API.
+ const wrapperEl = textLayerEl.parentElement!;
+ const wrapperRect = wrapperEl.getBoundingClientRect();
+
+ let domMatchOrdinal = 0;
+
+ for (const span of spans) {
+ const text = span.textContent || "";
+ if (text.length === 0) continue;
+ const lowerText = text.toLowerCase();
+ if (!lowerText.includes(lowerQuery)) continue;
+
+ // Find all match positions within this span
+ const matchPositions: number[] = [];
+ let pos = 0;
+ while (true) {
+ const idx = lowerText.indexOf(lowerQuery, pos);
+ if (idx === -1) break;
+ matchPositions.push(idx);
+ pos = idx + 1;
+ }
+ if (matchPositions.length === 0) continue;
+
+ const textNode = span.firstChild;
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) continue;
+
+ for (const idx of matchPositions) {
+ const isCurrentMatch =
+ domMatchOrdinal < pageMatches.length &&
+ allMatches.indexOf(pageMatches[domMatchOrdinal]) === currentMatchIndex;
+
+ try {
+ const range = document.createRange();
+ range.setStart(textNode, idx);
+ range.setEnd(textNode, Math.min(idx + lowerQueryLen, text.length));
+ const rects = range.getClientRects();
+
+ for (let ri = 0; ri < rects.length; ri++) {
+ const r = rects[ri];
+ const div = document.createElement("div");
+ div.className =
+ "search-highlight" + (isCurrentMatch ? " current" : "");
+ div.style.position = "absolute";
+ div.style.left = `${r.left - wrapperRect.left}px`;
+ div.style.top = `${r.top - wrapperRect.top}px`;
+ div.style.width = `${r.width}px`;
+ div.style.height = `${r.height}px`;
+ highlightLayerEl.appendChild(div);
+ }
+ } catch {
+ // Range errors can happen with stale text nodes
+ }
+
+ domMatchOrdinal++;
+ }
+ }
+
+ // Scroll current highlight into view only if not already visible
+ const currentHL = highlightLayerEl.querySelector(
+ ".search-highlight.current",
+ ) as HTMLElement;
+ if (currentHL) {
+ const scrollParent =
+ currentDisplayMode === "fullscreen"
+ ? document.querySelector(".canvas-container")
+ : null;
+ if (scrollParent) {
+ const sr = scrollParent.getBoundingClientRect();
+ const hr = currentHL.getBoundingClientRect();
+ if (hr.top < sr.top || hr.bottom > sr.bottom) {
+ currentHL.scrollIntoView({ block: "center", behavior: "smooth" });
+ }
+ } else {
+ // Inline mode: check visibility in viewport
+ const hr = currentHL.getBoundingClientRect();
+ if (hr.top < 0 || hr.bottom > window.innerHeight) {
+ currentHL.scrollIntoView({ block: "center", behavior: "smooth" });
+ }
+ }
+ }
+}
+
+function clearHighlights() {
+ highlightLayerEl.innerHTML = "";
+}
+
+function updateSearchUI() {
+ const hasQuery = searchQuery.length > 0;
+ if (allMatches.length === 0) {
+ searchMatchCountEl.textContent = hasQuery ? "No matches" : "";
+ } else {
+ searchMatchCountEl.textContent = `${currentMatchIndex + 1} of ${allMatches.length}`;
+ }
+ searchPrevBtn.disabled = allMatches.length === 0;
+ searchNextBtn.disabled = allMatches.length === 0;
+ // Hide nav controls when there's no query
+ const vis = hasQuery ? "" : "none";
+ searchMatchCountEl.style.display = vis;
+ searchPrevBtn.style.display = vis;
+ searchNextBtn.style.display = vis;
+}
+
+function openSearch() {
+ if (searchOpen) {
+ searchInputEl.focus();
+ searchInputEl.select();
+ return;
+ }
+ searchOpen = true;
+ searchBarEl.style.display = "flex";
+ updateSearchUI();
+ searchInputEl.focus();
+ requestFitToContent();
+ extractAllPageText();
+}
+
+function closeSearch() {
+ if (!searchOpen) return;
+ searchOpen = false;
+ searchBarEl.style.display = "none";
+ searchQuery = "";
+ searchInputEl.value = "";
+ allMatches = [];
+ currentMatchIndex = -1;
+ clearHighlights();
+ updateSearchUI();
+ requestFitToContent();
+}
+
+function toggleSearch() {
+ if (searchOpen) {
+ closeSearch();
+ } else {
+ openSearch();
+ }
+}
+
+function goToNextMatch() {
+ if (allMatches.length === 0) return;
+ currentMatchIndex = (currentMatchIndex + 1) % allMatches.length;
+ const match = allMatches[currentMatchIndex];
+ updateSearchUI();
+ if (match.pageNum !== currentPage) {
+ goToPage(match.pageNum);
+ } else {
+ renderHighlights();
+ }
+}
+
+function goToPrevMatch() {
+ if (allMatches.length === 0) return;
+ currentMatchIndex =
+ (currentMatchIndex - 1 + allMatches.length) % allMatches.length;
+ const match = allMatches[currentMatchIndex];
+ updateSearchUI();
+ if (match.pageNum !== currentPage) {
+ goToPage(match.pageNum);
+ } else {
+ renderHighlights();
+ }
+}
+
// Create app instance
// autoResize disabled - app fills its container, doesn't request size changes
const app = new App(
@@ -391,6 +669,8 @@ async function renderPage() {
textLayerEl.innerHTML = "";
textLayerEl.style.width = `${viewport.width}px`;
textLayerEl.style.height = `${viewport.height}px`;
+ // Set --scale-factor so CSS font-size/transform rules work correctly.
+ textLayerEl.style.setProperty("--scale-factor", `${scale}`);
// Render canvas - track the task so we can cancel it
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -430,6 +710,24 @@ async function renderPage() {
});
await textLayer.render();
+ // Cache page text items if not already cached
+ if (!pageTextItemsCache.has(pageToRender)) {
+ const items = (textContent.items as Array<{ str?: string }>).map(
+ (item) => item.str || "",
+ );
+ pageTextItemsCache.set(pageToRender, items);
+ pageTextCache.set(pageToRender, items.join(""));
+ }
+
+ // Size highlight layer to match canvas
+ highlightLayerEl.style.width = `${viewport.width}px`;
+ highlightLayerEl.style.height = `${viewport.height}px`;
+
+ // Re-render search highlights if search is active
+ if (searchOpen && searchQuery) {
+ renderHighlights();
+ }
+
updateControls();
updatePageContext();
@@ -549,8 +847,34 @@ prevBtn.addEventListener("click", prevPage);
nextBtn.addEventListener("click", nextPage);
zoomOutBtn.addEventListener("click", zoomOut);
zoomInBtn.addEventListener("click", zoomIn);
+searchBtn.addEventListener("click", toggleSearch);
+searchCloseBtn.addEventListener("click", closeSearch);
+searchPrevBtn.addEventListener("click", goToPrevMatch);
+searchNextBtn.addEventListener("click", goToNextMatch);
fullscreenBtn.addEventListener("click", toggleFullscreen);
+// Search input events
+searchInputEl.addEventListener("input", () => {
+ if (searchDebounceTimer) clearTimeout(searchDebounceTimer);
+ searchDebounceTimer = setTimeout(() => {
+ performSearch(searchInputEl.value);
+ }, 300);
+});
+
+searchInputEl.addEventListener("keydown", (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ if (e.shiftKey) {
+ goToPrevMatch();
+ } else {
+ goToNextMatch();
+ }
+ } else if (e.key === "Escape") {
+ e.preventDefault();
+ closeSearch();
+ }
+});
+
pageInputEl.addEventListener("change", () => {
const page = parseInt(pageInputEl.value, 10);
if (!isNaN(page)) {
@@ -568,6 +892,25 @@ pageInputEl.addEventListener("keydown", (e) => {
// Keyboard navigation
document.addEventListener("keydown", (e) => {
+ // Ctrl/Cmd+F: open our search if closed; if already focused, pass through to browser find
+ if ((e.ctrlKey || e.metaKey) && e.key === "f") {
+ if (!searchOpen) {
+ e.preventDefault();
+ openSearch();
+ } else if (document.activeElement === searchInputEl) {
+ // Already focused — close ours and let browser find open
+ closeSearch();
+ } else {
+ // Open but not focused — re-focus our search
+ e.preventDefault();
+ searchInputEl.focus();
+ searchInputEl.select();
+ }
+ return;
+ }
+
+ // Don't handle nav shortcuts when search input is focused
+ if (document.activeElement === searchInputEl) return;
if (document.activeElement === pageInputEl) return;
// Ctrl/Cmd+0 to reset zoom
@@ -579,7 +922,10 @@ document.addEventListener("keydown", (e) => {
switch (e.key) {
case "Escape":
- if (currentDisplayMode === "fullscreen") {
+ if (searchOpen) {
+ closeSearch();
+ e.preventDefault();
+ } else if (currentDisplayMode === "fullscreen") {
toggleFullscreen();
e.preventDefault();
}
diff --git a/examples/quickstart/mcp-app.html b/examples/quickstart/mcp-app.html
index bf276aa57..eef815c96 100644
--- a/examples/quickstart/mcp-app.html
+++ b/examples/quickstart/mcp-app.html
@@ -1,4 +1,4 @@
-
+
diff --git a/examples/say-server/mcp-app.html b/examples/say-server/mcp-app.html
index 1e2389bda..5a1dd3a9a 100644
--- a/examples/say-server/mcp-app.html
+++ b/examples/say-server/mcp-app.html
@@ -1,14 +1,14 @@
-
+
-
-
-
-
- Say View
-
-
-
-
-
-
+
+
+
+
+ Say View
+
+
+
+
+
+
diff --git a/examples/scenario-modeler-server/mcp-app.html b/examples/scenario-modeler-server/mcp-app.html
index ba12d4611..ee3a9791d 100644
--- a/examples/scenario-modeler-server/mcp-app.html
+++ b/examples/scenario-modeler-server/mcp-app.html
@@ -1,13 +1,13 @@
-
+
-
-
-
-
- SaaS Scenario Modeler
-
-
-
-
-
+
+
+
+
+ SaaS Scenario Modeler
+
+
+
+
+
diff --git a/examples/scenario-modeler-server/src/global.css b/examples/scenario-modeler-server/src/global.css
index 3599b9a6a..a64e4b3e3 100644
--- a/examples/scenario-modeler-server/src/global.css
+++ b/examples/scenario-modeler-server/src/global.css
@@ -2,10 +2,14 @@
box-sizing: border-box;
}
-html, body {
+html,
+body {
margin: 0;
padding: 0;
- font-family: system-ui, -apple-system, sans-serif;
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
font-size: 1rem;
}
diff --git a/examples/shadertoy-server/mcp-app.html b/examples/shadertoy-server/mcp-app.html
index 564597acb..abbb1534c 100644
--- a/examples/shadertoy-server/mcp-app.html
+++ b/examples/shadertoy-server/mcp-app.html
@@ -1,24 +1,44 @@
-
+
-
-
-
-
- ShaderToy Renderer
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ ShaderToy Renderer
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/shadertoy-server/src/global.css b/examples/shadertoy-server/src/global.css
index 97cda4404..1f3220223 100644
--- a/examples/shadertoy-server/src/global.css
+++ b/examples/shadertoy-server/src/global.css
@@ -2,8 +2,12 @@
box-sizing: border-box;
}
-html, body {
- font-family: system-ui, -apple-system, sans-serif;
+html,
+body {
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
font-size: 1rem;
}
diff --git a/examples/shadertoy-server/src/mcp-app.css b/examples/shadertoy-server/src/mcp-app.css
index 0449ea5ea..3f7c213f2 100644
--- a/examples/shadertoy-server/src/mcp-app.css
+++ b/examples/shadertoy-server/src/mcp-app.css
@@ -1,4 +1,5 @@
-html, body {
+html,
+body {
margin: 0;
width: 100%;
height: 100%;
@@ -68,7 +69,9 @@ html, body {
display: none; /* Hidden by default, shown when fullscreen available */
align-items: center;
justify-content: center;
- transition: background 0.2s, opacity 0.2s;
+ transition:
+ background 0.2s,
+ opacity 0.2s;
opacity: 0; /* Initially invisible, shown on View hover */
z-index: 100;
}
diff --git a/examples/sheet-music-server/mcp-app.html b/examples/sheet-music-server/mcp-app.html
index f9b530225..0a4c01b5d 100644
--- a/examples/sheet-music-server/mcp-app.html
+++ b/examples/sheet-music-server/mcp-app.html
@@ -1,27 +1,27 @@
-
+
-
-
-
-
- Sheet Music Viewer
-
-
-
-
-
+
+
+
+
+ Sheet Music Viewer
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
diff --git a/examples/sheet-music-server/src/global.css b/examples/sheet-music-server/src/global.css
index 97cda4404..1f3220223 100644
--- a/examples/sheet-music-server/src/global.css
+++ b/examples/sheet-music-server/src/global.css
@@ -2,8 +2,12 @@
box-sizing: border-box;
}
-html, body {
- font-family: system-ui, -apple-system, sans-serif;
+html,
+body {
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
font-size: 1rem;
}
diff --git a/examples/sheet-music-server/src/mcp-app.css b/examples/sheet-music-server/src/mcp-app.css
index 48435e342..dfe6c796c 100644
--- a/examples/sheet-music-server/src/mcp-app.css
+++ b/examples/sheet-music-server/src/mcp-app.css
@@ -71,7 +71,6 @@ body {
color: var(--color-danger);
}
-
/* Sheet Music Section */
.sheet-section {
background: var(--color-card-bg);
diff --git a/examples/system-monitor-server/mcp-app.html b/examples/system-monitor-server/mcp-app.html
index 67cc50e8c..7a9275993 100644
--- a/examples/system-monitor-server/mcp-app.html
+++ b/examples/system-monitor-server/mcp-app.html
@@ -1,61 +1,61 @@
-
+
-
-
-
-
- System Monitor
-
-
-
-
-
-
-
-
-
-
-
+
- Links
+
+
+
-
+
+
+
-
-
-
-
+
+
+
+
diff --git a/examples/debug-server/src/global.css b/examples/debug-server/src/global.css
index 188632626..2171a30e9 100644
--- a/examples/debug-server/src/global.css
+++ b/examples/debug-server/src/global.css
@@ -2,15 +2,20 @@
box-sizing: border-box;
}
-html, body {
- font-family: system-ui, -apple-system, sans-serif;
+html,
+body {
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
font-size: 14px;
margin: 0;
padding: 0;
}
code {
- font-family: ui-monospace, "SF Mono", Monaco, "Cascadia Code", Consolas, monospace;
+ font-family:
+ ui-monospace, "SF Mono", Monaco, "Cascadia Code", Consolas, monospace;
font-size: 0.9em;
background: rgba(0, 0, 0, 0.05);
padding: 0.1em 0.3em;
@@ -27,7 +32,9 @@ button {
cursor: pointer;
}
-input, select, button {
+input,
+select,
+button {
font-family: inherit;
font-size: inherit;
}
diff --git a/examples/debug-server/src/mcp-app.css b/examples/debug-server/src/mcp-app.css
index 79a74ed26..70f3a2b6f 100644
--- a/examples/debug-server/src/mcp-app.css
+++ b/examples/debug-server/src/mcp-app.css
@@ -123,17 +123,39 @@
margin-right: 0.5rem;
}
-.log-type.ontoolinput { color: var(--color-primary); }
-.log-type.ontoolinputpartial { color: #8b5cf6; }
-.log-type.ontoolresult { color: var(--color-success); }
-.log-type.ontoolcancelled { color: var(--color-warning); }
-.log-type.onhostcontextchanged { color: #7c3aed; }
-.log-type.onteardown { color: #f97316; }
-.log-type.oncalltool { color: #ec4899; }
-.log-type.onlisttools { color: #14b8a6; }
-.log-type.onerror { color: var(--color-error); }
-.log-type.connected { color: var(--color-success); }
-.log-type.update-context { color: #0891b2; }
+.log-type.ontoolinput {
+ color: var(--color-primary);
+}
+.log-type.ontoolinputpartial {
+ color: #8b5cf6;
+}
+.log-type.ontoolresult {
+ color: var(--color-success);
+}
+.log-type.ontoolcancelled {
+ color: var(--color-warning);
+}
+.log-type.onhostcontextchanged {
+ color: #7c3aed;
+}
+.log-type.onteardown {
+ color: #f97316;
+}
+.log-type.oncalltool {
+ color: #ec4899;
+}
+.log-type.onlisttools {
+ color: #14b8a6;
+}
+.log-type.onerror {
+ color: var(--color-error);
+}
+.log-type.connected {
+ color: var(--color-success);
+}
+.log-type.update-context {
+ color: #0891b2;
+}
.log-payload-preview {
color: #6b7280;
@@ -191,8 +213,12 @@
}
@media (prefers-color-scheme: dark) {
- .info-group dt { color: #d1d5db; }
- .info-group dd { color: #9ca3af; }
+ .info-group dt {
+ color: #d1d5db;
+ }
+ .info-group dd {
+ color: #9ca3af;
+ }
}
.styles-sample {
@@ -275,7 +301,9 @@
}
@media (prefers-color-scheme: dark) {
- .action-group h3 { color: #d1d5db; }
+ .action-group h3 {
+ color: #d1d5db;
+ }
}
.action-row {
diff --git a/examples/integration-server/mcp-app.html b/examples/integration-server/mcp-app.html
index 205ff4e7a..e6a1f7bc1 100644
--- a/examples/integration-server/mcp-app.html
+++ b/examples/integration-server/mcp-app.html
@@ -1,14 +1,14 @@
-
+
-
-
-
-
-
+
- Size
+
+
+
+
+
+
+
+
+
+ Current:
measuring...
+
-
+
+
+
-
-
+
Server Tools
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading globe...
-
+
diff --git a/examples/map-server/test-standalone.html b/examples/map-server/test-standalone.html
index 79bc0cac2..300826412 100644
--- a/examples/map-server/test-standalone.html
+++ b/examples/map-server/test-standalone.html
@@ -1,134 +1,144 @@
-
+
-
-
-
+
+
+
Loading CesiumJS...
-
+
diff --git a/examples/pdf-server/mcp-app.html b/examples/pdf-server/mcp-app.html
index cf217235d..8ee8b067e 100644
--- a/examples/pdf-server/mcp-app.html
+++ b/examples/pdf-server/mcp-app.html
@@ -11,7 +11,11 @@
Loading PDF...
-
+
-
-
- Waiting for notation...
-
+
+
+ Waiting for notation...
+
-
-
-
+
+
+ System Monitor
-
- Start
-
-
- Ready
-
-
- CPU Usage
-
-
-
-