@canopy-iiif/app 0.10.20 → 0.10.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/build/mdx.js CHANGED
@@ -247,6 +247,21 @@ function extractPlainText(mdxSource) {
247
247
  return content;
248
248
  }
249
249
 
250
+ function extractMarkdownSummary(mdxSource) {
251
+ let { content } = parseFrontmatter(String(mdxSource || ''));
252
+ if (!content) return '';
253
+ content = content.replace(/^import[^\n]+$/gm, ' ');
254
+ content = content.replace(/^export[^\n]+$/gm, ' ');
255
+ content = content.replace(/```[\s\S]*?```/g, ' ');
256
+ content = content.replace(/<[A-Za-z][^>]*?>[\s\S]*?<\/[A-Za-z][^>]*?>/g, ' ');
257
+ content = content.replace(/<[A-Za-z][^>]*?\/>/g, ' ');
258
+ content = content.replace(/\{\/[A-Za-z0-9_.-]+\}/g, ' ');
259
+ content = content.replace(/\{[^{}]*\}/g, ' ');
260
+ content = content.replace(/^#{1,6}\s+.*$/gm, ' ');
261
+ content = content.replace(/\s+/g, ' ').trim();
262
+ return content;
263
+ }
264
+
250
265
  function extractTitle(mdxSource) {
251
266
  const { data, content } = parseFrontmatter(String(mdxSource || ""));
252
267
  if (data && typeof data.title === "string" && data.title.trim()) {
@@ -1006,6 +1021,7 @@ module.exports = {
1006
1021
  extractTitle,
1007
1022
  extractHeadings,
1008
1023
  extractPlainText,
1024
+ extractMarkdownSummary,
1009
1025
  isReservedFile,
1010
1026
  parseFrontmatter,
1011
1027
  compileMdxFile,
@@ -8,12 +8,17 @@ function pagesToRecords(pageRecords) {
8
8
  .filter((p) => p && p.href && p.searchInclude)
9
9
  .map((p) => {
10
10
  const summary = typeof p.searchSummary === 'string' ? p.searchSummary.trim() : '';
11
+ const summaryMarkdown =
12
+ typeof p.searchSummaryMarkdown === 'string'
13
+ ? p.searchSummaryMarkdown.trim()
14
+ : '';
11
15
  const record = {
12
16
  title: p.title || p.href,
13
17
  href: rootRelativeHref(p.href),
14
18
  type: p.searchType || 'page',
15
19
  };
16
20
  if (summary) record.summaryValue = summary;
21
+ if (summaryMarkdown) record.summaryMarkdown = summaryMarkdown;
17
22
  return record;
18
23
  });
19
24
  }
@@ -210,6 +210,7 @@ async function collectMdxPageRecords() {
210
210
  if (base !== 'sitemap.mdx') {
211
211
  const href = rootRelativeHref(rel.split(path.sep).join('/'));
212
212
  const plainText = mdx.extractPlainText(src);
213
+ const markdownSummary = mdx.extractMarkdownSummary(src);
213
214
  const summary = plainText || '';
214
215
  const underSearch = /^search\//i.test(href) || href.toLowerCase() === 'search.html';
215
216
  let include = !underSearch;
@@ -234,6 +235,7 @@ async function collectMdxPageRecords() {
234
235
  searchInclude: include && !!trimmedType,
235
236
  searchType: trimmedType || undefined,
236
237
  searchSummary: summary,
238
+ searchSummaryMarkdown: markdownSummary,
237
239
  });
238
240
  }
239
241
  }
@@ -186,6 +186,18 @@ function groupLabel(type) {
186
186
  return type.charAt(0).toUpperCase() + type.slice(1);
187
187
  }
188
188
 
189
+ function hidePanel(panel) {
190
+ if (!panel) return;
191
+ panel.classList.add('is-empty');
192
+ panel.setAttribute('hidden', 'hidden');
193
+ }
194
+
195
+ function showPanel(panel) {
196
+ if (!panel) return;
197
+ panel.classList.remove('is-empty');
198
+ panel.removeAttribute('hidden');
199
+ }
200
+
189
201
  function getItems(list) {
190
202
  try {
191
203
  return Array.prototype.slice.call(list.querySelectorAll('[data-canopy-item]'));
@@ -196,7 +208,12 @@ function getItems(list) {
196
208
 
197
209
  function renderList(list, records, groupOrder) {
198
210
  list.innerHTML = '';
199
- if (!records.length) return;
211
+ const panel = list.closest('[data-canopy-search-form-panel]');
212
+ if (!records.length) {
213
+ hidePanel(panel);
214
+ return;
215
+ }
216
+ showPanel(panel);
200
217
  const groups = new Map();
201
218
  records.forEach((record) => {
202
219
  const type = String(record && record.type || 'page');
@@ -207,8 +224,8 @@ function renderList(list, records, groupOrder) {
207
224
  const orderedKeys = [...desiredOrder.filter((key) => groups.has(key)), ...Array.from(groups.keys()).filter((key) => !desiredOrder.includes(key))];
208
225
  orderedKeys.forEach((key) => {
209
226
  const header = document.createElement('div');
227
+ header.className = 'canopy-search-teaser__label';
210
228
  header.textContent = groupLabel(key);
211
- header.style.cssText = 'padding:6px 12px;font-weight:600;color:#374151';
212
229
  list.appendChild(header);
213
230
  const entries = groups.get(key) || [];
214
231
  entries.forEach((record) => {
@@ -217,44 +234,39 @@ function renderList(list, records, groupOrder) {
217
234
  item.setAttribute('data-canopy-item', '');
218
235
  item.href = href;
219
236
  item.tabIndex = 0;
220
- item.className = 'canopy-card canopy-card--teaser';
221
- item.style.cssText = 'display:flex;gap:12px;padding:8px 12px;text-decoration:none;color:#030712;border-radius:8px;align-items:center;outline:none;';
237
+ item.className = 'canopy-card canopy-card--teaser canopy-search-teaser__item';
222
238
 
223
239
  const showThumb = String(record && record.type || '') === 'work' && record && record.thumbnail;
224
240
  if (showThumb) {
225
241
  const media = document.createElement('div');
226
- media.style.cssText = 'flex:0 0 48px;height:48px;border-radius:6px;overflow:hidden;background:#f1f5f9;display:flex;align-items:center;justify-content:center;';
242
+ media.className = 'canopy-search-teaser__thumb';
227
243
  const img = document.createElement('img');
228
244
  img.src = record.thumbnail;
229
245
  img.alt = '';
230
246
  img.loading = 'lazy';
231
- img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
247
+ img.className = 'canopy-search-teaser__thumb-img';
232
248
  media.appendChild(img);
233
249
  item.appendChild(media);
234
250
  }
235
251
 
236
252
  const textWrap = document.createElement('div');
237
- textWrap.style.cssText = 'display:flex;flex-direction:column;gap:2px;min-width:0;';
253
+ textWrap.className = 'canopy-search-teaser__text';
238
254
  const title = document.createElement('span');
239
255
  title.textContent = record.title || record.href || '';
240
- title.style.cssText = 'font-weight:600;font-size:0.95rem;line-height:1.3;color:#111827;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
256
+ title.className = 'canopy-search-teaser__title';
241
257
  textWrap.appendChild(title);
242
258
  const meta = Array.isArray(record && record.metadata) ? record.metadata : [];
243
259
  if (meta.length) {
244
260
  const metaLine = document.createElement('span');
245
261
  metaLine.textContent = meta.slice(0, 2).join(' • ');
246
- metaLine.style.cssText = 'font-size:0.8rem;color:#475569;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
262
+ metaLine.className = 'canopy-search-teaser__meta';
247
263
  textWrap.appendChild(metaLine);
248
264
  }
249
265
  item.appendChild(textWrap);
250
266
 
251
- item.onmouseenter = () => { item.style.background = '#f8fafc'; };
252
- item.onmouseleave = () => { item.style.background = 'transparent'; };
253
267
  item.onfocus = () => {
254
- item.style.background = '#eef2ff';
255
268
  try { item.scrollIntoView({ block: 'nearest' }); } catch (_) {}
256
269
  };
257
- item.onblur = () => { item.style.background = 'transparent'; };
258
270
  list.appendChild(item);
259
271
  });
260
272
  });
@@ -275,15 +287,19 @@ function focusLast(list, resetTarget) {
275
287
  items[items.length - 1].focus();
276
288
  }
277
289
 
278
- function bindKeyboardNavigation({ input, list, panel }) {
290
+ function bindKeyboardNavigation({ input, list, panel, ensureResults }) {
291
+ const ensure = () => {
292
+ if (typeof ensureResults === 'function') ensureResults();
293
+ return getItems(list).length > 0;
294
+ };
279
295
  input.addEventListener('keydown', (event) => {
280
296
  if (event.key === 'ArrowDown') {
281
297
  event.preventDefault();
282
- panel.style.display = 'block';
298
+ if (!ensure()) return;
283
299
  focusFirst(list);
284
300
  } else if (event.key === 'ArrowUp') {
285
301
  event.preventDefault();
286
- panel.style.display = 'block';
302
+ if (!ensure()) return;
287
303
  focusLast(list, input);
288
304
  }
289
305
  });
@@ -311,7 +327,7 @@ function bindKeyboardNavigation({ input, list, panel }) {
311
327
  event.preventDefault();
312
328
  try { current.click(); } catch (_) {}
313
329
  } else if (event.key === 'Escape') {
314
- panel.style.display = 'none';
330
+ hidePanel(panel);
315
331
  try { input && input.focus && input.focus(); } catch (_) {}
316
332
  }
317
333
  });
@@ -336,7 +352,7 @@ async function attachSearchForm(host) {
336
352
  } catch (_) {}
337
353
  }
338
354
 
339
- if (onSearchPage) panel.style.display = 'none';
355
+ if (onSearchPage) hidePanel(panel);
340
356
 
341
357
  const list = (() => {
342
358
  try { return panel.querySelector('#cplist'); } catch (_) { return null; }
@@ -357,13 +373,7 @@ async function attachSearchForm(host) {
357
373
  const records = await loadRecords();
358
374
 
359
375
  function render(items) {
360
- list.innerHTML = '';
361
- if (!items.length) {
362
- panel.style.display = onSearchPage ? 'none' : 'block';
363
- return;
364
- }
365
376
  renderList(list, items, groupOrder);
366
- panel.style.display = 'block';
367
377
  }
368
378
 
369
379
  function filterAndShow(query) {
@@ -371,7 +381,7 @@ async function attachSearchForm(host) {
371
381
  const q = toLower(query);
372
382
  if (!q) {
373
383
  list.innerHTML = '';
374
- panel.style.display = onSearchPage ? 'none' : 'block';
384
+ hidePanel(panel);
375
385
  return;
376
386
  }
377
387
  const out = [];
@@ -404,18 +414,27 @@ async function attachSearchForm(host) {
404
414
  filterAndShow(input.value || '');
405
415
  });
406
416
 
407
- bindKeyboardNavigation({ input, list, panel });
417
+ bindKeyboardNavigation({
418
+ input,
419
+ list,
420
+ panel,
421
+ ensureResults: () => {
422
+ if (!getItems(list).length) {
423
+ filterAndShow(input.value || '');
424
+ }
425
+ },
426
+ });
408
427
 
409
428
  document.addEventListener('keydown', (event) => {
410
429
  if (event.key === 'Escape') {
411
- panel.style.display = 'none';
430
+ hidePanel(panel);
412
431
  }
413
432
  });
414
433
 
415
434
  document.addEventListener('mousedown', (event) => {
416
435
  try {
417
436
  if (!panel.contains(event.target) && !host.contains(event.target)) {
418
- panel.style.display = 'none';
437
+ hidePanel(panel);
419
438
  }
420
439
  } catch (_) {}
421
440
  });
@@ -433,7 +452,6 @@ async function attachSearchForm(host) {
433
452
  try { window.dispatchEvent(new CustomEvent('canopy:search:setQuery', { detail: { hotkey: true } })); } catch (_) {}
434
453
  return;
435
454
  }
436
- panel.style.display = 'block';
437
455
  if (input && input.focus) input.focus();
438
456
  filterAndShow(input && input.value || '');
439
457
  }
@@ -446,7 +464,6 @@ async function attachSearchForm(host) {
446
464
  try { window.dispatchEvent(new CustomEvent('canopy:search:setQuery', { detail: {} })); } catch (_) {}
447
465
  return;
448
466
  }
449
- panel.style.display = 'block';
450
467
  if (input && input.focus) input.focus();
451
468
  filterAndShow(input && input.value || '');
452
469
  }
@@ -354,6 +354,15 @@ function sanitizeRecordForDisplay(r) {
354
354
  const out = { ...base };
355
355
  if (out.metadata) delete out.metadata;
356
356
  if (out.summary) out.summary = toSafeString(out.summary, '');
357
+ const summaryMarkdown = toSafeString(
358
+ (r && r.summaryMarkdown) ||
359
+ (r && r.searchSummaryMarkdown) ||
360
+ (r && r.search && r.search.summaryMarkdown),
361
+ ''
362
+ ).trim();
363
+ if (summaryMarkdown) {
364
+ out.summaryMarkdown = summaryMarkdown;
365
+ }
357
366
  const hrefRaw = toSafeString(r && r.href, '');
358
367
  out.href = rootRelativeHref(hrefRaw);
359
368
  const thumbnail = toSafeString(r && r.thumbnail, '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "0.10.20",
3
+ "version": "0.10.22",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
package/ui/dist/index.mjs CHANGED
@@ -111,41 +111,158 @@ import React3, { useMemo } from "react";
111
111
  function escapeRegExp(str = "") {
112
112
  return String(str).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
113
113
  }
114
- function buildSnippet({ text = "", query = "", maxLength = 240 }) {
114
+ function buildSnippet({ text = "", query = "", maxLength = 360 }) {
115
115
  const clean = String(text || "").replace(/\s+/g, " ").trim();
116
116
  if (!clean) return "";
117
+ const safeMax = Math.max(60, Number(maxLength) || 360);
117
118
  const term = String(query || "").trim();
118
119
  if (!term)
119
- return clean.length > maxLength ? clean.slice(0, maxLength) + "\u2026" : clean;
120
+ return clean.length > safeMax ? clean.slice(0, safeMax).trimEnd() + "\u2026" : clean;
120
121
  const lower = clean.toLowerCase();
121
122
  const termLower = term.toLowerCase();
122
123
  const idx = lower.indexOf(termLower);
123
- if (idx === -1) {
124
- return clean.length > maxLength ? clean.slice(0, maxLength) + "\u2026" : clean;
124
+ if (idx === -1)
125
+ return clean.length > safeMax ? clean.slice(0, safeMax).trimEnd() + "\u2026" : clean;
126
+ const padding = Math.max(0, Math.floor((safeMax - term.length) / 2));
127
+ let start = Math.max(0, idx - padding);
128
+ let end = start + safeMax;
129
+ if (end > clean.length) {
130
+ end = clean.length;
131
+ start = Math.max(0, end - safeMax);
125
132
  }
126
- const context = Math.max(0, maxLength / 2);
127
- const start = Math.max(0, idx - context);
128
- const end = Math.min(clean.length, idx + term.length + context);
129
- let snippet = clean.slice(start, end);
133
+ let snippet = clean.slice(start, end).trim();
130
134
  if (start > 0) snippet = "\u2026" + snippet;
131
135
  if (end < clean.length) snippet = snippet + "\u2026";
132
136
  return snippet;
133
137
  }
134
- function highlightSnippet(snippet, query) {
135
- if (!query) return snippet;
138
+ function highlightTextNode(text, query, keyPrefix = "") {
139
+ if (!query) return text;
136
140
  const term = String(query).trim();
137
- if (!term) return snippet;
138
- const parts = String(snippet).split(
139
- new RegExp(`(${escapeRegExp(term)})`, "gi")
140
- );
141
+ if (!term) return text;
142
+ const regex = new RegExp(`(${escapeRegExp(term)})`, "gi");
143
+ const parts = String(text).split(regex);
141
144
  const termLower = term.toLowerCase();
142
- return parts.map(
143
- (part, idx) => part.toLowerCase() === termLower ? /* @__PURE__ */ React3.createElement("mark", { key: idx }, part) : /* @__PURE__ */ React3.createElement(React3.Fragment, { key: idx }, part)
144
- );
145
+ return parts.map((part, idx) => {
146
+ if (!part) return null;
147
+ if (part.toLowerCase() === termLower) {
148
+ return /* @__PURE__ */ React3.createElement("mark", { key: `${keyPrefix}-${idx}` }, part);
149
+ }
150
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, { key: `${keyPrefix}-${idx}` }, part);
151
+ });
152
+ }
153
+ function tokenizeInlineMarkdown(input = "") {
154
+ const tokens = [];
155
+ let text = input;
156
+ while (text.length) {
157
+ if (text.startsWith("\n")) {
158
+ tokens.push({ type: "break" });
159
+ text = text.slice(1);
160
+ continue;
161
+ }
162
+ if (text.startsWith("**")) {
163
+ const closing = text.indexOf("**", 2);
164
+ if (closing !== -1) {
165
+ const inner = text.slice(2, closing);
166
+ tokens.push({ type: "strong", children: tokenizeInlineMarkdown(inner) });
167
+ text = text.slice(closing + 2);
168
+ continue;
169
+ }
170
+ }
171
+ if (text.startsWith("__")) {
172
+ const closing = text.indexOf("__", 2);
173
+ if (closing !== -1) {
174
+ const inner = text.slice(2, closing);
175
+ tokens.push({ type: "strong", children: tokenizeInlineMarkdown(inner) });
176
+ text = text.slice(closing + 2);
177
+ continue;
178
+ }
179
+ }
180
+ if (text.startsWith("*")) {
181
+ if (!text.startsWith("**")) {
182
+ const closing = text.indexOf("*", 1);
183
+ if (closing !== -1) {
184
+ const inner = text.slice(1, closing);
185
+ tokens.push({ type: "em", children: tokenizeInlineMarkdown(inner) });
186
+ text = text.slice(closing + 1);
187
+ continue;
188
+ }
189
+ }
190
+ }
191
+ if (text.startsWith("_")) {
192
+ if (!text.startsWith("__")) {
193
+ const closing = text.indexOf("_", 1);
194
+ if (closing !== -1) {
195
+ const inner = text.slice(1, closing);
196
+ tokens.push({ type: "em", children: tokenizeInlineMarkdown(inner) });
197
+ text = text.slice(closing + 1);
198
+ continue;
199
+ }
200
+ }
201
+ }
202
+ if (text.startsWith("`")) {
203
+ const closing = text.indexOf("`", 1);
204
+ if (closing !== -1) {
205
+ const inner = text.slice(1, closing);
206
+ tokens.push({ type: "code", value: inner });
207
+ text = text.slice(closing + 1);
208
+ continue;
209
+ }
210
+ }
211
+ if (text.startsWith("[")) {
212
+ const endLabel = text.indexOf("]");
213
+ const startHref = endLabel !== -1 ? text.indexOf("(", endLabel) : -1;
214
+ const endHref = startHref !== -1 ? text.indexOf(")", startHref) : -1;
215
+ if (endLabel !== -1 && startHref === endLabel + 1 && endHref !== -1) {
216
+ const label = text.slice(1, endLabel);
217
+ const href = text.slice(startHref + 1, endHref);
218
+ tokens.push({
219
+ type: "link",
220
+ href,
221
+ children: tokenizeInlineMarkdown(label)
222
+ });
223
+ text = text.slice(endHref + 1);
224
+ continue;
225
+ }
226
+ }
227
+ const specials = ["**", "__", "*", "_", "`", "[", "\n"];
228
+ const nextIndex = specials.map(
229
+ (needle) => needle === "\n" ? text.indexOf("\n") : text.indexOf(needle)
230
+ ).filter((idx) => idx > 0).reduce((min, idx) => min === -1 || idx < min ? idx : min, -1);
231
+ if (nextIndex === -1) {
232
+ tokens.push({ type: "text", value: text });
233
+ break;
234
+ }
235
+ tokens.push({ type: "text", value: text.slice(0, nextIndex) });
236
+ text = text.slice(nextIndex);
237
+ }
238
+ return tokens;
239
+ }
240
+ function renderMarkdownTokens(tokens, query, keyPrefix = "token") {
241
+ return tokens.map((token, idx) => {
242
+ const key = `${keyPrefix}-${idx}`;
243
+ switch (token.type) {
244
+ case "strong":
245
+ return /* @__PURE__ */ React3.createElement("strong", { key }, renderMarkdownTokens(token.children || [], query, key));
246
+ case "em":
247
+ return /* @__PURE__ */ React3.createElement("em", { key }, renderMarkdownTokens(token.children || [], query, key));
248
+ case "code":
249
+ return /* @__PURE__ */ React3.createElement("code", { key }, token.value);
250
+ case "link":
251
+ return /* @__PURE__ */ React3.createElement("a", { key, href: token.href, target: "_blank", rel: "noreferrer" }, renderMarkdownTokens(token.children || [], query, key));
252
+ case "break":
253
+ return /* @__PURE__ */ React3.createElement("br", { key });
254
+ case "text":
255
+ default:
256
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, { key }, highlightTextNode(token.value || "", query, key));
257
+ }
258
+ });
145
259
  }
146
260
  function formatDisplayUrl(href = "") {
147
261
  try {
148
- const url = new URL(href, href.startsWith("http") ? void 0 : "http://example.com");
262
+ const url = new URL(
263
+ href,
264
+ href.startsWith("http") ? void 0 : "http://example.com"
265
+ );
149
266
  if (!href.startsWith("http")) return href;
150
267
  const displayPath = url.pathname.replace(/\/$/, "");
151
268
  return `${url.host}${displayPath}${url.search}`.replace(/\/$/, "");
@@ -158,21 +275,22 @@ function ArticleCard({
158
275
  title = "Untitled",
159
276
  annotation = "",
160
277
  summary = "",
278
+ summaryMarkdown = "",
161
279
  metadata = [],
162
280
  query = ""
163
281
  }) {
164
- const snippetSource = annotation || summary;
282
+ const snippetSource = summaryMarkdown || annotation || summary;
165
283
  const snippet = useMemo(
166
284
  () => buildSnippet({ text: snippetSource, query }),
167
285
  [snippetSource, query]
168
286
  );
169
- const highlighted = useMemo(
170
- () => highlightSnippet(snippet, query),
171
- [snippet, query]
287
+ const snippetTokens = useMemo(
288
+ () => tokenizeInlineMarkdown(snippet),
289
+ [snippet]
172
290
  );
173
291
  const metaList = Array.isArray(metadata) ? metadata.map((m) => String(m || "")).filter(Boolean) : [];
174
292
  const displayUrl = useMemo(() => formatDisplayUrl(href), [href]);
175
- return /* @__PURE__ */ React3.createElement("a", { href, className: "canopy-article-card" }, /* @__PURE__ */ React3.createElement("article", null, displayUrl ? /* @__PURE__ */ React3.createElement("p", { className: "canopy-article-card__url" }, displayUrl) : null, /* @__PURE__ */ React3.createElement("h3", null, title), snippet ? /* @__PURE__ */ React3.createElement("p", { className: "canopy-article-card__snippet" }, highlighted) : null, metaList.length ? /* @__PURE__ */ React3.createElement("ul", { className: "canopy-article-card__meta" }, metaList.slice(0, 3).map((item, idx) => /* @__PURE__ */ React3.createElement("li", { key: `${item}-${idx}` }, item))) : null));
293
+ return /* @__PURE__ */ React3.createElement("a", { href, className: "canopy-article-card" }, /* @__PURE__ */ React3.createElement("article", null, /* @__PURE__ */ React3.createElement("h3", null, title), displayUrl ? /* @__PURE__ */ React3.createElement("span", { className: "canopy-article-card__url" }, displayUrl) : null, snippet ? /* @__PURE__ */ React3.createElement("p", { className: "canopy-article-card__snippet" }, renderMarkdownTokens(snippetTokens, query)) : null, metaList.length ? /* @__PURE__ */ React3.createElement("ul", { className: "canopy-article-card__meta" }, metaList.slice(0, 3).map((item, idx) => /* @__PURE__ */ React3.createElement("li", { key: `${item}-${idx}` }, item))) : null));
176
294
  }
177
295
 
178
296
  // ui/src/layout/Grid.jsx
@@ -494,15 +612,16 @@ function SearchPanelForm(props = {}) {
494
612
  import React10 from "react";
495
613
  function SearchPanelTeaserResults(props = {}) {
496
614
  const { style, className } = props || {};
497
- const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
615
+ const classes = ["canopy-search-teaser", "is-empty", className].filter(Boolean).join(" ");
498
616
  return /* @__PURE__ */ React10.createElement(
499
617
  "div",
500
618
  {
501
619
  "data-canopy-search-form-panel": true,
620
+ hidden: true,
502
621
  className: classes || void 0,
503
622
  style
504
623
  },
505
- /* @__PURE__ */ React10.createElement("div", { id: "cplist" })
624
+ /* @__PURE__ */ React10.createElement("div", { id: "cplist", className: "canopy-search-teaser__list" })
506
625
  );
507
626
  }
508
627
 
@@ -1235,6 +1354,7 @@ function DefaultArticleTemplate({ record, query }) {
1235
1354
  title: record.title || record.href || "Untitled",
1236
1355
  annotation: record.annotation,
1237
1356
  summary: record.summary || record.summaryValue || "",
1357
+ summaryMarkdown: record.summaryMarkdown || record.summary || record.summaryValue || "",
1238
1358
  metadata,
1239
1359
  query
1240
1360
  }