@glossarist/concept-browser 0.7.104 → 0.7.106
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/package.json +3 -1
- package/scripts/generate-data.mjs +6 -0
- package/scripts/smoke.mjs +269 -0
- package/src/__tests__/components/rake-bundles.test.ts +20 -0
- package/src/__tests__/composables/use-concept-edges-defensive.test.ts +164 -0
- package/src/__tests__/concept-detail-hydration.test.ts +199 -0
- package/src/__tests__/utils/external-detection.test.ts +85 -0
- package/src/components/ConceptDetail.vue +45 -38
- package/src/components/PartitiveRelationDiagram.vue +12 -0
- package/src/components/relation-sphere/rake-bundles.ts +17 -0
- package/src/composables/use-concept-edges.ts +59 -43
- package/src/config/types.ts +17 -1
- package/src/layouts/Default.astro +41 -16
- package/src/pages/[...path].astro +6 -0
- package/src/utils/external-detection.ts +74 -0
package/src/config/types.ts
CHANGED
|
@@ -18,6 +18,22 @@ export interface LogoConfig {
|
|
|
18
18
|
localDark?: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export interface FaviconConfig {
|
|
22
|
+
/** Path prefix for all favicon URLs. Defaults to '/', set to a sub-path
|
|
23
|
+
* when deploying under BASE_PATH != '/'. */
|
|
24
|
+
base_path?: string;
|
|
25
|
+
/** Skip emitting the default <link rel="icon"> and <link rel="apple-touch-icon">
|
|
26
|
+
* tags. Use when the consumer provides their own RFG-compatible markup via
|
|
27
|
+
* `links_html`. Default: false (emit default tags). */
|
|
28
|
+
skip_default_links?: boolean;
|
|
29
|
+
/** Raw HTML string (one or more <link> tags) to emit in <head> instead of
|
|
30
|
+
* the default favicon block. RealFaviconGenerator output goes here. */
|
|
31
|
+
links_html?: string;
|
|
32
|
+
/** Consumer-side directory (relative to cwd) holding canonical favicon files.
|
|
33
|
+
* The CLI's favicon step copies these into public/, overriding defaults. */
|
|
34
|
+
source_dir?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
export interface SiteBranding {
|
|
22
38
|
primaryColor?: string;
|
|
23
39
|
darkColor?: string;
|
|
@@ -27,7 +43,7 @@ export interface SiteBranding {
|
|
|
27
43
|
};
|
|
28
44
|
logo?: LogoConfig;
|
|
29
45
|
footerLogo?: LogoConfig;
|
|
30
|
-
favicon?: string;
|
|
46
|
+
favicon?: string | FaviconConfig;
|
|
31
47
|
ownerName?: string;
|
|
32
48
|
ownerUrl?: string;
|
|
33
49
|
}
|
|
@@ -36,28 +36,53 @@ const fontHeader = brandingFonts?.header?.family
|
|
|
36
36
|
const fontBody = brandingFonts?.body?.family
|
|
37
37
|
? `'${brandingFonts.body.family}', system-ui, sans-serif`
|
|
38
38
|
: `'DM Sans', system-ui, sans-serif`;
|
|
39
|
+
|
|
40
|
+
// Favicon configuration (issue #173).
|
|
41
|
+
//
|
|
42
|
+
// Consumers provide their own favicon set in one of two ways:
|
|
43
|
+
//
|
|
44
|
+
// 1. Drop canonical files at `branding.favicon.source_dir` (relative
|
|
45
|
+
// to the consumer's cwd). The CLI's existing favicon step already
|
|
46
|
+
// copies these into public/ overriding the defaults — this layout
|
|
47
|
+
// just needs to emit the right <link> tags.
|
|
48
|
+
//
|
|
49
|
+
// 2. Set `branding.favicon.skip_default_links: true` and inject
|
|
50
|
+
// custom <link> tags via the `branding.favicon.links_html`
|
|
51
|
+
// string (e.g. RealFaviconGenerator output). The layout emits
|
|
52
|
+
// that verbatim and skips the default tag block.
|
|
53
|
+
//
|
|
54
|
+
// When neither is set, today's default behavior is preserved.
|
|
55
|
+
const faviconCfg = siteConfig?.branding?.favicon ?? {};
|
|
56
|
+
const skipDefaultFaviconLinks = faviconCfg.skip_default_links === true;
|
|
57
|
+
const customFaviconLinks: string = faviconCfg.links_html ?? '';
|
|
58
|
+
const faviconBase = faviconCfg.base_path ?? '/';
|
|
39
59
|
---
|
|
40
60
|
|
|
41
61
|
<!DOCTYPE html>
|
|
42
62
|
<html lang="en">
|
|
43
63
|
<head>
|
|
44
64
|
<meta charset="utf-8" />
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
{!skipDefaultFaviconLinks && (
|
|
66
|
+
<>
|
|
67
|
+
<link rel="icon" type="image/x-icon" href={`${faviconBase}favicon.ico`} />
|
|
68
|
+
<link rel="icon" type="image/svg+xml" href={`${faviconBase}favicon.svg`} />
|
|
69
|
+
<link rel="icon" type="image/png" sizes="16x16" href={`${faviconBase}favicon-16x16.png`} />
|
|
70
|
+
<link rel="icon" type="image/png" sizes="32x32" href={`${faviconBase}favicon-32x32.png`} />
|
|
71
|
+
<link rel="icon" type="image/png" sizes="48x48" href={`${faviconBase}favicon-48x48.png`} />
|
|
72
|
+
<link rel="apple-touch-icon" sizes="57x57" href={`${faviconBase}apple-touch-icon-57x57.png`} />
|
|
73
|
+
<link rel="apple-touch-icon" sizes="60x60" href={`${faviconBase}apple-touch-icon-60x60.png`} />
|
|
74
|
+
<link rel="apple-touch-icon" sizes="72x72" href={`${faviconBase}apple-touch-icon-72x72.png`} />
|
|
75
|
+
<link rel="apple-touch-icon" sizes="76x76" href={`${faviconBase}apple-touch-icon-76x76.png`} />
|
|
76
|
+
<link rel="apple-touch-icon" sizes="114x114" href={`${faviconBase}apple-touch-icon-114x114.png`} />
|
|
77
|
+
<link rel="apple-touch-icon" sizes="120x120" href={`${faviconBase}apple-touch-icon-120x120.png`} />
|
|
78
|
+
<link rel="apple-touch-icon" sizes="144x144" href={`${faviconBase}apple-touch-icon-144x144.png`} />
|
|
79
|
+
<link rel="apple-touch-icon" sizes="152x152" href={`${faviconBase}apple-touch-icon-152x152.png`} />
|
|
80
|
+
<link rel="apple-touch-icon" sizes="167x167" href={`${faviconBase}apple-touch-icon-167x167.png`} />
|
|
81
|
+
<link rel="apple-touch-icon" sizes="180x180" href={`${faviconBase}apple-touch-icon-180x180.png`} />
|
|
82
|
+
<link rel="apple-touch-icon" sizes="1024x1024" href={`${faviconBase}apple-touch-icon-1024x1024.png`} />
|
|
83
|
+
</>
|
|
84
|
+
)}
|
|
85
|
+
{customFaviconLinks && <Fragment set:html={customFaviconLinks} />}
|
|
61
86
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
62
87
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
63
88
|
<meta name="apple-mobile-web-app-title" content={siteConfig.title} />
|
|
@@ -11,6 +11,12 @@ export async function getStaticPaths() {
|
|
|
11
11
|
{ params: { path: 'about' } },
|
|
12
12
|
{ params: { path: 'contributors' } },
|
|
13
13
|
{ params: { path: 'news' } },
|
|
14
|
+
/* /learn and sub-routes — page-types.ts auto-synthesizes the nav entry.
|
|
15
|
+
Without these static paths the nav link 404s on prefetch (issue #174). */
|
|
16
|
+
{ params: { path: 'learn' } },
|
|
17
|
+
{ params: { path: 'learn/relationships' } },
|
|
18
|
+
{ params: { path: 'learn/designations' } },
|
|
19
|
+
{ params: { path: 'learn/statuses' } },
|
|
14
20
|
];
|
|
15
21
|
for (const ds of datasets) {
|
|
16
22
|
paths.push({ params: { path: `dataset/${ds.data.id}/about` } });
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External concept detection — ISO 704:2022 external concepts.
|
|
3
|
+
*
|
|
4
|
+
* An "external concept" is one referenced from this dataset but defined
|
|
5
|
+
* elsewhere (status: 'external'). In diagrams, external concepts are
|
|
6
|
+
* shown in parentheses with a dashed border per ISO 704:2022 §5.5.4.3.1.
|
|
7
|
+
*
|
|
8
|
+
* Detection requires concept resolution (looking up the referenced
|
|
9
|
+
* concept's status), which is a consumer concern — the model itself
|
|
10
|
+
* stays pure. These utility functions take a conceptStore/lookup
|
|
11
|
+
* callback provided by the caller.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface ConceptLike {
|
|
15
|
+
status?: string | null;
|
|
16
|
+
relatedConcepts?: ReadonlyArray<{ type?: string | null }>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ConceptStoreLike {
|
|
20
|
+
lookup(ref: { source?: string | null; id?: string | null }): ConceptLike | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RefLike {
|
|
24
|
+
source?: string | null;
|
|
25
|
+
id?: string | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if a member's concept resolves to status: external.
|
|
30
|
+
*/
|
|
31
|
+
export function isExternalConcept(
|
|
32
|
+
ref: RefLike | null | undefined,
|
|
33
|
+
store: ConceptStoreLike | null | undefined,
|
|
34
|
+
): boolean {
|
|
35
|
+
if (!ref || !store) return false;
|
|
36
|
+
const concept = store.lookup(ref);
|
|
37
|
+
return concept?.status === 'external';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if an external concept has a provided_by edge for resolution.
|
|
42
|
+
* Without provided_by, the reference dangles — the decomposition is
|
|
43
|
+
* incomplete.
|
|
44
|
+
*/
|
|
45
|
+
export function hasProvidedBy(
|
|
46
|
+
ref: RefLike | null | undefined,
|
|
47
|
+
store: ConceptStoreLike | null | undefined,
|
|
48
|
+
): boolean {
|
|
49
|
+
if (!ref || !store) return false;
|
|
50
|
+
const concept = store.lookup(ref);
|
|
51
|
+
if (!concept?.relatedConcepts) return false;
|
|
52
|
+
return concept.relatedConcepts.some(rc => rc.type === 'provided_by');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if an external concept's reference dangles (no provided_by edge).
|
|
57
|
+
*/
|
|
58
|
+
export function isDanglingExternal(
|
|
59
|
+
ref: RefLike | null | undefined,
|
|
60
|
+
store: ConceptStoreLike | null | undefined,
|
|
61
|
+
): boolean {
|
|
62
|
+
return isExternalConcept(ref, store) && !hasProvidedBy(ref, store);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Format a label for an external concept — parenthetical per ISO 704.
|
|
67
|
+
* Returns `"(label)"` for external concepts, `label` for internal.
|
|
68
|
+
*/
|
|
69
|
+
export function formatExternalLabel(
|
|
70
|
+
label: string,
|
|
71
|
+
isExternal: boolean,
|
|
72
|
+
): string {
|
|
73
|
+
return isExternal ? `(${label})` : label;
|
|
74
|
+
}
|