@c4a/context-cli 0.7.64 → 0.7.66

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/cli.js CHANGED
@@ -87071,6 +87071,50 @@ function siteArticleSources(article, registry2, repositoryRemote) {
87071
87071
  }
87072
87072
  return [...new Map(sources.map((source2) => [JSON.stringify(source2), source2])).values()];
87073
87073
  }
87074
+ function siteSourceLinks(article, registry2, repositoryRemote) {
87075
+ const candidates = new Map;
87076
+ const names = new Map;
87077
+ const add = (map2, key, url) => map2.set(key, (map2.get(key) ?? new Set).add(url));
87078
+ for (const reference2 of article?.sections.flatMap((section) => section.references) ?? []) {
87079
+ const ref2 = reference2.source_ref;
87080
+ const local = parseRepoContentRef(ref2);
87081
+ const repo = local ? undefined : registry2.repos.find((entry) => ref2 === `repo:${entry.id}` || ref2 === `repo:${entry.name}`);
87082
+ const prefix = repo?.subpath?.replace(/^\/+|\/+$/gu, "") ?? "";
87083
+ const base = repo && repoUrl(repo.remote);
87084
+ const target = (path3, directory) => local ? local.worktree ? undefined : repoContentWebUrl(repositoryRemote, local.commit, path3, directory) : base ? `${base}/${directory ? "tree" : "blob"}/${encodeURIComponent(repo.ref)}/${encodedPath(path3)}` : undefined;
87085
+ const path2 = reference2.locator.path;
87086
+ const parts = path2.split("/");
87087
+ if (!local && !repo || path2.startsWith("/") || parts.some((part) => !part || part === "." || part === ".."))
87088
+ continue;
87089
+ for (const written of new Set([path2, prefix ? `${prefix}/${path2}` : path2])) {
87090
+ const segments = written.split("/");
87091
+ for (let depth = segments.length;depth >= 1; depth--) {
87092
+ const spelled = segments.slice(0, depth).join("/");
87093
+ const full = prefix && !written.startsWith(`${prefix}/`) ? `${prefix}/${spelled}` : spelled;
87094
+ const directory = depth < segments.length;
87095
+ const url = target(local ? spelled : full, directory);
87096
+ if (!url)
87097
+ break;
87098
+ if (!directory)
87099
+ add(candidates, spelled, url);
87100
+ else if (depth >= 2) {
87101
+ add(candidates, spelled, url);
87102
+ add(candidates, `${spelled}/`, url);
87103
+ }
87104
+ const name3 = segments[depth - 1];
87105
+ if (directory && /[._-]/u.test(name3))
87106
+ add(names, name3, url);
87107
+ }
87108
+ }
87109
+ }
87110
+ const links = {};
87111
+ for (const map2 of [names, candidates]) {
87112
+ for (const [key, urls] of map2)
87113
+ if (urls.size === 1)
87114
+ links[key] = [...urls][0];
87115
+ }
87116
+ return links;
87117
+ }
87074
87118
  var encodedPath = (value) => value.split("/").map(encodeURIComponent).join("/");
87075
87119
  var init_packageSiteSources = __esm(() => {
87076
87120
  init_src2();
@@ -87593,6 +87637,29 @@ var init_packageSiteTheme = __esm(() => {
87593
87637
  const render = md.renderer.rules[name];
87594
87638
  if (render) md.renderer.rules[name] = (...args) => render(...args).replace(/\{/g, '&#123;');
87595
87639
  }
87640
+ // The compiler lists contextSourceLinks only from the page's recorded references.
87641
+ // Headings stay unlinked because their text also feeds the outline links.
87642
+ md.core.ruler.push('context_source_links', state => {
87643
+ const links = state.env.frontmatter && state.env.frontmatter.contextSourceLinks;
87644
+ if (!links || typeof links !== 'object') return;
87645
+ state.tokens.forEach((block, position) => {
87646
+ if (block.type !== 'inline' || !block.children || state.tokens[position - 1]?.type === 'heading_open') return;
87647
+ const children = [];
87648
+ let depth = 0;
87649
+ for (const token of block.children) {
87650
+ if (token.type === 'link_open') depth++;
87651
+ if (token.type === 'link_close') depth--;
87652
+ const href = token.type === 'code_inline' && depth === 0 && Object.hasOwn(links, token.content) ? links[token.content] : null;
87653
+ if (typeof href !== 'string' || !/^https?:\/\//.test(href)) { children.push(token); continue; }
87654
+ const open = new state.Token('html_inline', '', 0);
87655
+ open.content = '<a class="context-source-link" href="' + md.utils.escapeHtml(href) + '" target="_blank" rel="noopener noreferrer">';
87656
+ const close = new state.Token('html_inline', '', 0);
87657
+ close.content = '</a>';
87658
+ children.push(open, token, close);
87659
+ }
87660
+ block.children = children;
87661
+ });
87662
+ });
87596
87663
  md.block.ruler.before('paragraph', 'context_anchor', (state, line, end, silent) => {
87597
87664
  const raw = state.src.slice(state.bMarks[line] + state.tShift[line], state.eMarks[line]);
87598
87665
  const match = /^<a id="(section-[a-zA-Z0-9_%.-]+)"><\/a>$/.exec(raw);
@@ -87642,7 +87709,7 @@ import DefaultTheme from 'vitepress/theme-without-fonts';
87642
87709
  import { useData, useRoute, dataSymbol, onContentUpdated, getScrollOffset } from 'vitepress';
87643
87710
  import { onMounted, onUnmounted, nextTick, watch, computed, ref, provide, h } from 'vue';
87644
87711
  import './style.css';
87645
- import extensions from './extensions.js';
87712
+ import extensions, { preloadExtensions } from './extensions.js';
87646
87713
  export default {
87647
87714
  extends: DefaultTheme,
87648
87715
  Layout: {
@@ -87829,7 +87896,10 @@ export default {
87829
87896
  // components that read isDark are mounted again for readers starting dark.
87830
87897
  const startedDark = ref(false);
87831
87898
  const custom = name => extensions[name]({ key: startedDark.value ? 'client' : 'prerender' });
87832
- onMounted(() => { mounted.value = true; startedDark.value = data.isDark.value; });
87899
+ onMounted(() => {
87900
+ mounted.value = true;
87901
+ if (data.isDark.value) preloadExtensions().catch(() => {}).then(() => { startedDark.value = true; });
87902
+ });
87833
87903
  const updatedLabel = computed(() => {
87834
87904
  const value = data.frontmatter.value.contextUpdated || data.theme.value.contextUpdated;
87835
87905
  if (!value) return '';
@@ -88233,6 +88303,12 @@ ${readerContentStyles(".vp-doc")}
88233
88303
  .context-sources li + li { margin-top: 6px; }
88234
88304
  .context-sources a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
88235
88305
  .context-sources a:hover { color: var(--vp-c-brand-1); border-color: currentColor; }
88306
+ .vp-doc a.context-source-link { font-weight: inherit; border-bottom: 0; }
88307
+ .vp-doc a.context-source-link::before { content: ""; display: inline-block; width: .85em; height: .85em; margin-right: .25em; vertical-align: -.1em; background: currentColor; opacity: .55; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71'/%3E%3Cpath d='M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71'/%3E%3Cpath d='M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71'/%3E%3C/svg%3E") center / contain no-repeat; transition: opacity .15s; }
88308
+ .vp-doc a.context-source-link > code { border-bottom: 1px dotted color-mix(in srgb, var(--vp-c-text-1) 55%, transparent); transition: color .15s, border-color .15s; }
88309
+ .vp-doc td a.context-source-link { white-space: nowrap; }
88310
+ .vp-doc a.context-source-link:hover::before { opacity: 1; }
88311
+ .vp-doc a.context-source-link:hover > code { color: var(--vp-c-brand-1); border-bottom-style: solid; border-bottom-color: currentColor; }
88236
88312
  /* Section landing pages: two cards per row, one on narrow screens. */
88237
88313
  .context-section-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-top: 24px; }
88238
88314
  .vp-doc .context-section-card { min-width: 0; padding: 18px 20px; border: 1px solid var(--vp-c-divider); border-radius: 12px;
@@ -88357,6 +88433,7 @@ async function writeSiteExtensions(projectRoot, temporary, extensions) {
88357
88433
  }
88358
88434
  const imports = ["import { h, defineAsyncComponent, resolveComponent } from 'vue';"];
88359
88435
  const slots = [];
88436
+ const remounted = [];
88360
88437
  for (const [name3, path2] of Object.entries(extensions?.slots ?? {})) {
88361
88438
  if (path2 === undefined)
88362
88439
  continue;
@@ -88365,11 +88442,15 @@ async function writeSiteExtensions(projectRoot, temporary, extensions) {
88365
88442
  continue;
88366
88443
  }
88367
88444
  imports.push(`const ${name3} = defineAsyncComponent(() => import(${JSON.stringify(`../../_site/${path2}`)}));`);
88445
+ if (name3 !== "floating")
88446
+ remounted.push(name3);
88368
88447
  slots.push(`${JSON.stringify(name3)}: ${name3 === "floating" ? `() => h(resolveComponent('ClientOnly'), null, { default: () => h(${name3}) })` : `props => h(${name3}, props)`}`);
88369
88448
  }
88449
+ const preload = `export const preloadExtensions = () => Promise.all([${remounted.map((name3) => `${name3}.__asyncLoader()`).join(",")}]);`;
88370
88450
  await writeFile20(join70(temporary, ".vitepress/theme/extensions.js"), imports.join(`
88371
88451
  `) + `
88372
88452
  export default {${slots.join(",")}};
88453
+ ${preload}
88373
88454
  `);
88374
88455
  for (const [key, path2] of Object.entries(extensions?.pages ?? {})) {
88375
88456
  await mkdir25(join70(temporary, "custom"), { recursive: true });
@@ -90080,13 +90161,15 @@ async function writePackageSite(input) {
90080
90161
  const pageFrontmatter = (page, content3, titles) => {
90081
90162
  const original = sourceContent.get(page.package_path);
90082
90163
  const sources = siteArticleSources(original?.article, registry2, repositoryRemote);
90164
+ const sourceLinks = siteSourceLinks(original?.article, registry2, repositoryRemote);
90083
90165
  const timestamp = parseKnowledgeFrontmatter(original?.content ?? content3).timestamp;
90084
90166
  const updated = typeof timestamp === "string" && Number.isFinite(Date.parse(timestamp)) ? new Date(timestamp).toISOString() : null;
90085
90167
  return `---
90086
90168
  title: ${JSON.stringify(page.title)}
90087
90169
  ${titles ? `contextTitles: ${JSON.stringify(titles)}
90088
90170
  ` : ""}` + `contextSources: ${JSON.stringify(sources)}
90089
- contextUpdated: ${JSON.stringify(updated)}
90171
+ ` + `${Object.keys(sourceLinks).length ? `contextSourceLinks: ${JSON.stringify(sourceLinks)}
90172
+ ` : ""}contextUpdated: ${JSON.stringify(updated)}
90090
90173
  ---
90091
90174
  `;
90092
90175
  };