@glossarist/concept-browser 0.7.50 → 0.7.51
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 +1 -1
- package/src/adapters/non-verbal/figure-bridge.ts +22 -23
- package/src/adapters/non-verbal/formula-bridge.ts +11 -9
- package/src/adapters/non-verbal/glossarist-augment.d.ts +126 -0
- package/src/adapters/non-verbal/index.ts +12 -9
- package/src/adapters/non-verbal/kind.ts +2 -1
- package/src/adapters/non-verbal/table-bridge.ts +12 -10
- package/src/adapters/non-verbal/types.ts +36 -54
- package/src/adapters/non-verbal-resolver.ts +4 -2
- package/src/components/NonVerbalRepDisplay.vue +2 -2
- package/src/components/figure/FigureDisplay.vue +16 -15
- package/src/components/figure/FigureImages.vue +38 -16
- package/src/components/figure/figure-image-pick.ts +1 -1
- package/src/components/figure/figure-layout.ts +1 -1
- package/src/components/formula/FormulaDisplay.vue +11 -9
- package/src/components/formula/FormulaExpression.vue +4 -4
- package/src/components/non-verbal/NonVerbalCaption.vue +5 -5
- package/src/components/non-verbal/NonVerbalSources.vue +3 -11
- package/src/components/table/TableDisplay.vue +6 -4
- package/src/components/table/TableMarkup.vue +1 -1
- package/src/composables/use-non-verbal-entity.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.51",
|
|
4
4
|
"description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* ambiguity with the HTML `<img alt>` attribute.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import
|
|
27
|
-
import { isType, pickField, pickFieldArray,
|
|
26
|
+
import { Figure, FigureImage } from 'glossarist';
|
|
27
|
+
import { isType, pickField, pickFieldArray, localized } from './prefix';
|
|
28
28
|
import { sourcesFromJsonLd } from './source-bridge';
|
|
29
29
|
|
|
30
30
|
const FORMAT_SET: ReadonlySet<string> = new Set(['svg', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'avif']);
|
|
@@ -35,18 +35,20 @@ function imageFromJsonLd(raw: Record<string, unknown>): FigureImage | null {
|
|
|
35
35
|
const src = pickField<string>(raw, 'src');
|
|
36
36
|
if (!src) return null;
|
|
37
37
|
const formatRaw = (pickField<string>(raw, 'format') ?? '').toLowerCase();
|
|
38
|
-
const format =
|
|
38
|
+
const format = FORMAT_SET.has(formatRaw) ? formatRaw : 'svg';
|
|
39
39
|
const roleRaw = pickField<string>(raw, 'role');
|
|
40
|
-
const role = roleRaw && ROLE_SET.has(roleRaw) ?
|
|
40
|
+
const role = roleRaw && ROLE_SET.has(roleRaw) ? roleRaw : undefined;
|
|
41
41
|
const width = pickField<number>(raw, 'width');
|
|
42
42
|
const height = pickField<number>(raw, 'height');
|
|
43
43
|
const scale = pickField<number>(raw, 'scale');
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
return new FigureImage({
|
|
45
|
+
src,
|
|
46
|
+
format,
|
|
47
|
+
...(role !== undefined && { role }),
|
|
48
|
+
...(typeof width === 'number' && { width }),
|
|
49
|
+
...(typeof height === 'number' && { height }),
|
|
50
|
+
...(typeof scale === 'number' && { scale }),
|
|
51
|
+
});
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
function imagesFromJsonLd(raw: unknown): FigureImage[] {
|
|
@@ -85,17 +87,14 @@ export function figureFromJsonLd(doc: Record<string, unknown>): Figure | null {
|
|
|
85
87
|
const subfigures = subfiguresFromJsonLd(pickField(doc, 'subfigure'));
|
|
86
88
|
const sources = sourcesFromJsonLd(pickField(doc, 'source'));
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
void pickFieldRecord;
|
|
99
|
-
|
|
100
|
-
return fig;
|
|
90
|
+
return new Figure({
|
|
91
|
+
id,
|
|
92
|
+
images,
|
|
93
|
+
...(identifier && { identifier }),
|
|
94
|
+
...(caption && { caption }),
|
|
95
|
+
...(alt && { alt }),
|
|
96
|
+
...(description && { description }),
|
|
97
|
+
...(subfigures && { subfigures }),
|
|
98
|
+
...(sources.length && { sources }),
|
|
99
|
+
});
|
|
101
100
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* }
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import
|
|
18
|
+
import { Formula } from 'glossarist';
|
|
19
19
|
import { isType, pickField, localized } from './prefix';
|
|
20
20
|
import { sourcesFromJsonLd } from './source-bridge';
|
|
21
21
|
|
|
@@ -31,18 +31,20 @@ export function formulaFromJsonLd(doc: Record<string, unknown>): Formula | null
|
|
|
31
31
|
if (!expression) return null;
|
|
32
32
|
|
|
33
33
|
const notationRaw = (pickField<string>(doc, 'notation') ?? '').toLowerCase();
|
|
34
|
-
const notation = NOTATION_SET.has(notationRaw) ?
|
|
34
|
+
const notation = NOTATION_SET.has(notationRaw) ? notationRaw : 'latex';
|
|
35
35
|
|
|
36
36
|
const identifier = pickField<string>(doc, 'identifier');
|
|
37
37
|
const caption = localized(doc, 'caption');
|
|
38
38
|
const description = localized(doc, 'description');
|
|
39
39
|
const sources = sourcesFromJsonLd(pickField(doc, 'source'));
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
return new Formula({
|
|
42
|
+
id,
|
|
43
|
+
expression,
|
|
44
|
+
notation,
|
|
45
|
+
...(identifier && { identifier }),
|
|
46
|
+
...(caption && { caption }),
|
|
47
|
+
...(description && { description }),
|
|
48
|
+
...(sources.length && { sources }),
|
|
49
|
+
});
|
|
48
50
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Local module augmentation for glossarist 0.4.2.
|
|
2
|
+
//
|
|
3
|
+
// Upstream's published src/models/index.d.ts declares ZERO classes for the
|
|
4
|
+
// non-verbal hierarchy (Figure, Table, Formula, FigureImage, NonVerbalEntity,
|
|
5
|
+
// SharedNonVerbalEntity, NonVerbalReference + subclasses, BibliographyEntry,
|
|
6
|
+
// BibliographyData) plus the localized-string helpers. The top-level
|
|
7
|
+
// index.d.ts re-exports the names, so TypeScript silently resolves every
|
|
8
|
+
// consumer import to `any`.
|
|
9
|
+
//
|
|
10
|
+
// This file declares the runtime shape so consumer code can be type-checked.
|
|
11
|
+
// DELETE this file when upstream ships proper declarations — tracked by
|
|
12
|
+
// PR glossarist/glossarist-js#31 (targets v0.4.3+).
|
|
13
|
+
|
|
14
|
+
import type { ConceptSource, GlossaristModel } from 'glossarist';
|
|
15
|
+
|
|
16
|
+
declare module 'glossarist' {
|
|
17
|
+
class RegistrableModel extends GlossaristModel {
|
|
18
|
+
static register(type: string, cls: typeof RegistrableModel): void;
|
|
19
|
+
static fromData(data: Record<string, unknown>): RegistrableModel;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class FigureImage extends GlossaristModel {
|
|
23
|
+
constructor(data?: {
|
|
24
|
+
src?: string | null;
|
|
25
|
+
format?: string | null;
|
|
26
|
+
role?: string | null;
|
|
27
|
+
width?: number | null;
|
|
28
|
+
height?: number | null;
|
|
29
|
+
scale?: number | null;
|
|
30
|
+
});
|
|
31
|
+
readonly src: string | null;
|
|
32
|
+
readonly format: string | null;
|
|
33
|
+
readonly role: string | null;
|
|
34
|
+
readonly width: number | null;
|
|
35
|
+
readonly height: number | null;
|
|
36
|
+
readonly scale: number | null;
|
|
37
|
+
static fromJSON(data: Record<string, unknown>): FigureImage;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class NonVerbalEntity extends RegistrableModel {
|
|
41
|
+
constructor(data?: Record<string, unknown>);
|
|
42
|
+
readonly caption: Record<string, string> | null;
|
|
43
|
+
readonly description: Record<string, string> | null;
|
|
44
|
+
readonly alt: Record<string, string> | null;
|
|
45
|
+
readonly sources: ConceptSource[];
|
|
46
|
+
findById(targetId: string): NonVerbalEntity | null;
|
|
47
|
+
allIds(): string[];
|
|
48
|
+
static fromJSON(data: Record<string, unknown>): NonVerbalEntity;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class SharedNonVerbalEntity extends NonVerbalEntity {
|
|
52
|
+
constructor(data?: Record<string, unknown>);
|
|
53
|
+
readonly id: string | null;
|
|
54
|
+
readonly identifier: string | null;
|
|
55
|
+
findById(targetId: string): SharedNonVerbalEntity | null;
|
|
56
|
+
allIds(): string[];
|
|
57
|
+
static fromJSON(data: Record<string, unknown>): SharedNonVerbalEntity;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class Figure extends SharedNonVerbalEntity {
|
|
61
|
+
constructor(data?: Record<string, unknown>);
|
|
62
|
+
readonly images: FigureImage[];
|
|
63
|
+
readonly subfigures: Figure[];
|
|
64
|
+
findById(targetId: string): Figure | null;
|
|
65
|
+
allIds(): string[];
|
|
66
|
+
static fromJSON(data: Record<string, unknown>): Figure;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class Table extends SharedNonVerbalEntity {
|
|
70
|
+
constructor(data?: Record<string, unknown>);
|
|
71
|
+
readonly content: Record<string, unknown> | null;
|
|
72
|
+
readonly format: string | null;
|
|
73
|
+
static fromJSON(data: Record<string, unknown>): Table;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class Formula extends SharedNonVerbalEntity {
|
|
77
|
+
constructor(data?: Record<string, unknown>);
|
|
78
|
+
readonly expression: Record<string, string> | null;
|
|
79
|
+
readonly notation: string | null;
|
|
80
|
+
static fromJSON(data: Record<string, unknown>): Formula;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const NON_VERBAL_TYPES: readonly string[];
|
|
84
|
+
|
|
85
|
+
class NonVerbalReference extends RegistrableModel {
|
|
86
|
+
constructor(data?: Record<string, unknown>);
|
|
87
|
+
readonly entityId: string | null;
|
|
88
|
+
readonly display: string | null;
|
|
89
|
+
readonly dedupKey: readonly [string, string | null];
|
|
90
|
+
static fromJSON(data: Record<string, unknown> | string): NonVerbalReference;
|
|
91
|
+
static register(type: string, cls: typeof NonVerbalReference): void;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class FigureReference extends NonVerbalReference {
|
|
95
|
+
static fromJSON(data: Record<string, unknown> | string): FigureReference;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class TableReference extends NonVerbalReference {
|
|
99
|
+
static fromJSON(data: Record<string, unknown> | string): TableReference;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class FormulaReference extends NonVerbalReference {
|
|
103
|
+
static fromJSON(data: Record<string, unknown> | string): FormulaReference;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
class BibliographyEntry extends GlossaristModel {
|
|
107
|
+
constructor(data?: Record<string, unknown>);
|
|
108
|
+
readonly id: string | null;
|
|
109
|
+
readonly reference: string | null;
|
|
110
|
+
readonly title: string | null;
|
|
111
|
+
readonly link: string | null;
|
|
112
|
+
readonly type: string | null;
|
|
113
|
+
static fromJSON(data: Record<string, unknown>): BibliographyEntry;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
class BibliographyData extends GlossaristModel {
|
|
117
|
+
constructor(data?: Record<string, unknown>);
|
|
118
|
+
readonly entries: BibliographyEntry[];
|
|
119
|
+
find(id: string): BibliographyEntry | null;
|
|
120
|
+
readonly keys: string[];
|
|
121
|
+
toYAML(): string;
|
|
122
|
+
toJSON(): { bibliography: BibliographyEntry[] };
|
|
123
|
+
static fromYAML(yamlString: string): BibliographyData;
|
|
124
|
+
static fromJSON(data: Record<string, unknown>): BibliographyData;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public API for the non-verbal entity model layer.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Model classes (Figure, Table, Formula, FigureImage, NonVerbalEntity) are
|
|
5
|
+
* re-exported from `glossarist` — upstream is the SSOT for the model.
|
|
6
|
+
* Consumer-owned types live in `./types`.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
export type {
|
|
10
10
|
LocalizedString,
|
|
11
11
|
NonVerbalKind,
|
|
12
|
-
FigureImage,
|
|
13
12
|
FigureImageFormat,
|
|
14
13
|
FigureImageRole,
|
|
15
14
|
NonVerbalSource,
|
|
16
15
|
NonVerbalSourceOrigin,
|
|
17
16
|
NonVerbalSourceRef,
|
|
18
17
|
NonVerbalSourceLocality,
|
|
19
|
-
NonVerbalEntityBase,
|
|
20
|
-
Figure,
|
|
21
|
-
Table,
|
|
22
18
|
TableContent,
|
|
23
19
|
TableFormat,
|
|
24
|
-
Formula,
|
|
25
20
|
FormulaNotation,
|
|
26
|
-
NonVerbalEntity,
|
|
27
21
|
NonVerbRepV3,
|
|
28
22
|
NonVerbalReference,
|
|
29
23
|
} from './types';
|
|
30
24
|
|
|
25
|
+
export type {
|
|
26
|
+
Figure,
|
|
27
|
+
FigureImage,
|
|
28
|
+
Table,
|
|
29
|
+
Formula,
|
|
30
|
+
NonVerbalEntity,
|
|
31
|
+
SharedNonVerbalEntity,
|
|
32
|
+
} from 'glossarist';
|
|
33
|
+
|
|
31
34
|
export { figureFromJsonLd } from './figure-bridge';
|
|
32
35
|
export { tableFromJsonLd } from './table-bridge';
|
|
33
36
|
export { formulaFromJsonLd } from './formula-bridge';
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* }
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import
|
|
23
|
+
import { Table } from 'glossarist';
|
|
24
|
+
import type { TableContent } from './types';
|
|
24
25
|
import { isType, pickField, localized } from './prefix';
|
|
25
26
|
import { sourcesFromJsonLd } from './source-bridge';
|
|
26
27
|
|
|
@@ -83,16 +84,17 @@ export function tableFromJsonLd(doc: Record<string, unknown>): Table | null {
|
|
|
83
84
|
if (!content) return null;
|
|
84
85
|
|
|
85
86
|
const formatRaw = (pickField<string>(doc, 'format') ?? '').toLowerCase();
|
|
86
|
-
const format = FORMAT_SET.has(formatRaw) ?
|
|
87
|
+
const format = FORMAT_SET.has(formatRaw) ? formatRaw : undefined;
|
|
87
88
|
|
|
88
89
|
const sources = sourcesFromJsonLd(pickField(doc, 'source'));
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
return new Table({
|
|
92
|
+
id,
|
|
93
|
+
content: content as Record<string, unknown>,
|
|
94
|
+
...(identifier && { identifier }),
|
|
95
|
+
...(caption && { caption }),
|
|
96
|
+
...(description && { description }),
|
|
97
|
+
...(format && { format }),
|
|
98
|
+
...(sources.length && { sources }),
|
|
99
|
+
});
|
|
98
100
|
}
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* glossarist-ruby model.
|
|
2
|
+
* Consumer-side types for non-verbal entities.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* corresponds to a field in the authoritative source.
|
|
4
|
+
* Model classes (Figure, Table, Formula, FigureImage, NonVerbalEntity) are
|
|
5
|
+
* imported from `glossarist` — the upstream library is the SSOT for the
|
|
6
|
+
* model. This file holds only what is genuinely consumer-owned:
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* - `NonVerbalKind`: routing discriminator used by the resolver, the
|
|
9
|
+
* anchor scheme, the mention dispatcher, and the section router.
|
|
10
|
+
* - `NonVerbRepV3`: local view of NonVerbRep's V3 shape. Upstream's
|
|
11
|
+
* published .d.ts still describes the pre-V3 `ref`/`text` shape; this
|
|
12
|
+
* interface lets the consumer type-check against runtime reality.
|
|
13
|
+
* Delete when upstream ships a corrected declaration.
|
|
14
|
+
* - `NonVerbalReference`: consumer-side view of inline mentions like
|
|
15
|
+
* `{{fig:foo}}`. Carries a `kind` for UI routing.
|
|
16
|
+
* - `LocalizedString`, `FigureImageFormat`, `FigureImageRole`,
|
|
17
|
+
* `TableFormat`, `TableContent`, `FormulaNotation`: string-union
|
|
18
|
+
* refinements the consumer validates at bridge time.
|
|
19
|
+
* - `NonVerbalSource*`: wire shape for JSON-LD source entries. Stays
|
|
20
|
+
* consumer-side until upstream ships a V3 NonVerbalSource model.
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
23
|
export type LocalizedString = Record<string, string>;
|
|
@@ -23,15 +28,6 @@ export type FigureImageFormat = 'svg' | 'png' | 'jpg' | 'jpeg' | 'gif' | 'webp'
|
|
|
23
28
|
|
|
24
29
|
export type FigureImageRole = 'vector' | 'raster' | 'dark' | 'light' | 'print';
|
|
25
30
|
|
|
26
|
-
export interface FigureImage {
|
|
27
|
-
src: string;
|
|
28
|
-
format: FigureImageFormat;
|
|
29
|
-
role?: FigureImageRole;
|
|
30
|
-
width?: number;
|
|
31
|
-
height?: number;
|
|
32
|
-
scale?: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
31
|
export interface NonVerbalSourceRef {
|
|
36
32
|
source?: string;
|
|
37
33
|
id?: string;
|
|
@@ -62,56 +58,42 @@ export interface NonVerbalSource {
|
|
|
62
58
|
origin?: NonVerbalSourceOrigin;
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
export interface NonVerbalEntityBase {
|
|
66
|
-
id: string;
|
|
67
|
-
identifier?: string;
|
|
68
|
-
caption?: LocalizedString;
|
|
69
|
-
description?: LocalizedString;
|
|
70
|
-
alt?: LocalizedString;
|
|
71
|
-
sources?: NonVerbalSource[];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface Figure extends NonVerbalEntityBase {
|
|
75
|
-
kind: 'figure';
|
|
76
|
-
images: FigureImage[];
|
|
77
|
-
subfigures?: Figure[];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
61
|
export type TableFormat = 'html' | 'markdown' | 'asciidoc';
|
|
81
62
|
|
|
82
63
|
export type TableContent =
|
|
83
64
|
| { kind: 'structured'; headers: LocalizedString[]; rows: LocalizedString[][] }
|
|
84
65
|
| { kind: 'markup'; markup: LocalizedString };
|
|
85
66
|
|
|
86
|
-
export interface Table extends NonVerbalEntityBase {
|
|
87
|
-
kind: 'table';
|
|
88
|
-
content: TableContent;
|
|
89
|
-
format?: TableFormat;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
67
|
export type FormulaNotation = 'latex' | 'mathml' | 'asciimath';
|
|
93
68
|
|
|
94
|
-
export interface Formula extends NonVerbalEntityBase {
|
|
95
|
-
kind: 'formula';
|
|
96
|
-
expression: LocalizedString;
|
|
97
|
-
notation: FormulaNotation;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export type NonVerbalEntity = Figure | Table | Formula;
|
|
101
|
-
|
|
102
69
|
/**
|
|
103
70
|
* V3 NonVerbRep runtime shape.
|
|
104
71
|
*
|
|
105
72
|
* glossarist-js's runtime `NonVerbRep` (post-V3 reshape) holds the same
|
|
106
|
-
* localized fields as
|
|
73
|
+
* localized fields as the base NonVerbalEntity plus a `type` discriminator
|
|
107
74
|
* and an `images[]` array. The published `.d.ts` (still stale at v0.4.2)
|
|
108
75
|
* describes the pre-V3 `ref`/`text` shape; this local interface lets
|
|
109
76
|
* consumer code type-check against reality. Drop when upstream ships a
|
|
110
|
-
* corrected `.d.ts
|
|
77
|
+
* corrected `.d.ts` (glossarist/glossarist-js#31).
|
|
111
78
|
*/
|
|
112
|
-
export interface NonVerbRepV3
|
|
79
|
+
export interface NonVerbRepV3 {
|
|
80
|
+
id: string;
|
|
81
|
+
identifier?: string | null;
|
|
113
82
|
type: string | null;
|
|
114
|
-
|
|
83
|
+
caption?: LocalizedString | null;
|
|
84
|
+
description?: LocalizedString | null;
|
|
85
|
+
alt?: LocalizedString | null;
|
|
86
|
+
images: NonVerbRepImage[];
|
|
87
|
+
sources?: NonVerbalSource[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface NonVerbRepImage {
|
|
91
|
+
src: string;
|
|
92
|
+
format?: string | null;
|
|
93
|
+
role?: string | null;
|
|
94
|
+
width?: number | null;
|
|
95
|
+
height?: number | null;
|
|
96
|
+
scale?: number | null;
|
|
115
97
|
}
|
|
116
98
|
|
|
117
99
|
export interface NonVerbalReference {
|
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
* exactly one instance per app.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type {
|
|
18
|
+
import type { NonVerbalKind } from './non-verbal/types';
|
|
19
|
+
import type { NonVerbalEntity } from 'glossarist';
|
|
19
20
|
import { KIND_TO_DIR, KIND_TO_BRIDGE } from './non-verbal/kind';
|
|
20
21
|
import { anchorId } from '../utils/non-verbal-anchor';
|
|
21
22
|
|
|
22
|
-
export type {
|
|
23
|
+
export type { NonVerbalKind } from './non-verbal/types';
|
|
24
|
+
export type { NonVerbalEntity } from 'glossarist';
|
|
23
25
|
|
|
24
26
|
export interface NonVerbalEntityResolverOptions {
|
|
25
27
|
basePath?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue';
|
|
3
3
|
import type { NonVerbRep, Citation } from 'glossarist';
|
|
4
|
-
import type {
|
|
4
|
+
import type { LocalizedString, NonVerbRepV3, NonVerbRepImage, NonVerbalSource } from '../adapters/non-verbal/types';
|
|
5
5
|
import { resolveFallbackChain } from '../utils/locale';
|
|
6
6
|
import FigureImages from './figure/FigureImages.vue';
|
|
7
7
|
import NonVerbalCaption from './non-verbal/NonVerbalCaption.vue';
|
|
@@ -21,7 +21,7 @@ const fallbackChain = computed(() => resolveFallbackChain(props.datasetLocales))
|
|
|
21
21
|
// (images/alt/caption/description/sources). See TODO.figures/19.
|
|
22
22
|
const v3Reps = computed<NonVerbRepV3[]>(() => props.reps as unknown as NonVerbRepV3[]);
|
|
23
23
|
|
|
24
|
-
function imagesOf(rep: NonVerbRepV3):
|
|
24
|
+
function imagesOf(rep: NonVerbRepV3): NonVerbRepImage[] {
|
|
25
25
|
return rep.images ?? [];
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* so `{{fig:id}}` mentions can scroll to it via the cross-ref composable.
|
|
11
11
|
*/
|
|
12
12
|
import { computed, ref } from 'vue';
|
|
13
|
-
import type { Figure } from '
|
|
13
|
+
import type { Figure } from 'glossarist';
|
|
14
14
|
import { useNonVerbalEntity } from '../../composables/use-non-verbal-entity';
|
|
15
15
|
import { resolveFallbackChain } from '../../utils/locale';
|
|
16
16
|
import { anchorId } from '../../utils/non-verbal-anchor';
|
|
@@ -34,39 +34,40 @@ const id = () => props.entityId;
|
|
|
34
34
|
const { entity, state, error } = useNonVerbalEntity(k, ds, id);
|
|
35
35
|
|
|
36
36
|
const fallbackChain = computed(() => resolveFallbackChain(props.datasetLocales));
|
|
37
|
-
const
|
|
37
|
+
const fig = computed(() => entity.value as Figure | null);
|
|
38
|
+
const layout = computed(() => fig.value ? deriveLayout(fig.value) : 'single');
|
|
38
39
|
const anchor = computed(() => anchorId('figure', props.datasetId, props.entityId));
|
|
39
40
|
const descriptionId = computed(() => `${anchor.value}-desc`);
|
|
40
41
|
|
|
41
|
-
const topLevelImages = computed(() =>
|
|
42
|
+
const topLevelImages = computed(() => fig.value?.images ?? []);
|
|
42
43
|
</script>
|
|
43
44
|
|
|
44
45
|
<template>
|
|
45
46
|
<figure
|
|
46
|
-
v-if="
|
|
47
|
+
v-if="fig && state === 'loaded'"
|
|
47
48
|
:id="anchor"
|
|
48
49
|
:class="`figure figure--${layout}`"
|
|
49
50
|
>
|
|
50
51
|
<div v-if="topLevelImages.length" :class="`figure__media figure__media--${layout}`">
|
|
51
52
|
<FigureImages
|
|
52
53
|
:images="topLevelImages"
|
|
53
|
-
:alt="
|
|
54
|
+
:alt="fig.alt"
|
|
54
55
|
:dataset-id="datasetId"
|
|
55
56
|
:locale="locale"
|
|
56
57
|
:fallback-chain="fallbackChain"
|
|
57
|
-
:hasDescription="!!
|
|
58
|
+
:hasDescription="!!fig.description && Object.keys(fig.description ?? {}).length > 0"
|
|
58
59
|
:description-id="descriptionId"
|
|
59
60
|
entity-label="Figure"
|
|
60
61
|
/>
|
|
61
62
|
</div>
|
|
62
63
|
|
|
63
|
-
<template v-if="
|
|
64
|
+
<template v-if="fig.subfigures?.length">
|
|
64
65
|
<div :class="`figure__subfigures figure__subfigures--${layout}`">
|
|
65
66
|
<FigureDisplay
|
|
66
|
-
v-for="sub in
|
|
67
|
-
:key="sub.id"
|
|
67
|
+
v-for="sub in fig.subfigures"
|
|
68
|
+
:key="sub.id ?? ''"
|
|
68
69
|
:dataset-id="datasetId"
|
|
69
|
-
:entity-id="sub.id"
|
|
70
|
+
:entity-id="sub.id ?? ''"
|
|
70
71
|
:locale="locale"
|
|
71
72
|
:dataset-locales="datasetLocales"
|
|
72
73
|
/>
|
|
@@ -74,17 +75,17 @@ const topLevelImages = computed(() => (entity.value as Figure | null)?.images ??
|
|
|
74
75
|
</template>
|
|
75
76
|
|
|
76
77
|
<NonVerbalCaption
|
|
77
|
-
:identifier="
|
|
78
|
-
:caption="
|
|
79
|
-
:description="
|
|
78
|
+
:identifier="fig.identifier"
|
|
79
|
+
:caption="fig.caption"
|
|
80
|
+
:description="fig.description"
|
|
80
81
|
:locale="locale"
|
|
81
82
|
:fallback-chain="fallbackChain"
|
|
82
83
|
:description-id="descriptionId"
|
|
83
84
|
/>
|
|
84
85
|
|
|
85
86
|
<NonVerbalSources
|
|
86
|
-
v-if="
|
|
87
|
-
:sources="
|
|
87
|
+
v-if="fig.sources?.length"
|
|
88
|
+
:sources="fig.sources"
|
|
88
89
|
/>
|
|
89
90
|
</figure>
|
|
90
91
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue';
|
|
3
|
-
import type { FigureImage
|
|
3
|
+
import type { FigureImage } from 'glossarist';
|
|
4
|
+
import type { LocalizedString, NonVerbRepImage } from '../../adapters/non-verbal/types';
|
|
4
5
|
import { pickLocaleText, hasLocale } from '../../utils/locale';
|
|
5
6
|
import { getFactory } from '../../adapters/factory';
|
|
6
7
|
|
|
8
|
+
type ImageLike = FigureImage | NonVerbRepImage;
|
|
9
|
+
|
|
7
10
|
const props = withDefaults(defineProps<{
|
|
8
|
-
images:
|
|
9
|
-
alt?: LocalizedString;
|
|
11
|
+
images: ImageLike[];
|
|
12
|
+
alt?: LocalizedString | null;
|
|
10
13
|
datasetId: string;
|
|
11
14
|
locale: string;
|
|
12
15
|
fallbackChain?: readonly string[];
|
|
@@ -19,7 +22,19 @@ const props = withDefaults(defineProps<{
|
|
|
19
22
|
|
|
20
23
|
const resolver = getFactory().nonVerbalResolver;
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
function imgSrc(img: ImageLike): string {
|
|
26
|
+
return typeof img.src === 'string' ? img.src : '';
|
|
27
|
+
}
|
|
28
|
+
function imgFormat(img: ImageLike): string {
|
|
29
|
+
const f = img.format;
|
|
30
|
+
return typeof f === 'string' ? f : 'svg';
|
|
31
|
+
}
|
|
32
|
+
function imgRole(img: ImageLike): string | null {
|
|
33
|
+
const r = img.role;
|
|
34
|
+
return typeof r === 'string' ? r : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const altText = computed(() => pickLocaleText(props.alt ?? undefined, props.locale, props.fallbackChain));
|
|
23
38
|
const altMissing = computed(() => !props.alt || Object.keys(props.alt).length === 0);
|
|
24
39
|
const altEmpty = computed(() =>
|
|
25
40
|
!!props.alt && hasLocale(props.alt, props.locale) && props.alt[props.locale] === '',
|
|
@@ -30,26 +45,33 @@ interface SourceVariant { src: string; type: string; media?: string; }
|
|
|
30
45
|
const sourceVariants = computed<SourceVariant[]>(() => {
|
|
31
46
|
const out: SourceVariant[] = [];
|
|
32
47
|
for (const img of props.images) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
48
|
+
const role = imgRole(img);
|
|
49
|
+
if (!role || role === 'vector' || role === 'raster') continue;
|
|
50
|
+
const src = imgSrc(img);
|
|
51
|
+
const format = imgFormat(img);
|
|
52
|
+
if (!src) continue;
|
|
53
|
+
const type = format === 'svg' ? 'image/svg+xml' : `image/${format === 'jpg' ? 'jpeg' : format}`;
|
|
36
54
|
let media: string | undefined;
|
|
37
|
-
if (
|
|
38
|
-
else if (
|
|
39
|
-
else if (
|
|
40
|
-
out.push({ src, type, media });
|
|
55
|
+
if (role === 'dark') media = '(prefers-color-scheme: dark)';
|
|
56
|
+
else if (role === 'light') media = '(prefers-color-scheme: light)';
|
|
57
|
+
else if (role === 'print') media = 'print';
|
|
58
|
+
out.push({ src: resolver.resolveImageUrl(props.datasetId, src), type, media });
|
|
41
59
|
}
|
|
42
60
|
return out;
|
|
43
61
|
});
|
|
44
62
|
|
|
45
63
|
const defaultImg = computed(() => {
|
|
46
|
-
const nonRole = props.images.find(i =>
|
|
64
|
+
const nonRole = props.images.find(i => {
|
|
65
|
+
const r = imgRole(i);
|
|
66
|
+
return !r || r === 'vector' || r === 'raster';
|
|
67
|
+
});
|
|
47
68
|
const chosen = nonRole ?? props.images[0];
|
|
48
|
-
|
|
69
|
+
const src = chosen ? imgSrc(chosen) : '';
|
|
70
|
+
if (!src) return null;
|
|
49
71
|
return {
|
|
50
|
-
src: resolver.resolveImageUrl(props.datasetId,
|
|
51
|
-
width: chosen
|
|
52
|
-
height: chosen
|
|
72
|
+
src: resolver.resolveImageUrl(props.datasetId, src),
|
|
73
|
+
width: chosen?.width ?? undefined,
|
|
74
|
+
height: chosen?.height ?? undefined,
|
|
53
75
|
};
|
|
54
76
|
});
|
|
55
77
|
</script>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* The `print` role is reserved for print stylesheets (selected via CSS
|
|
12
12
|
* @media print in FigureImages.vue), not picked here.
|
|
13
13
|
*/
|
|
14
|
-
import type { FigureImage } from '
|
|
14
|
+
import type { FigureImage } from 'glossarist';
|
|
15
15
|
|
|
16
16
|
export interface PickOptions {
|
|
17
17
|
prefersDark?: boolean;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Authors who want different behavior can split a composite figure into
|
|
13
13
|
* multiple top-level figures. V1 does not expose a `layout` field.
|
|
14
14
|
*/
|
|
15
|
-
import type { Figure } from '
|
|
15
|
+
import type { Figure } from 'glossarist';
|
|
16
16
|
|
|
17
17
|
export type FigureLayout = 'single' | 'row' | 'column' | 'grid';
|
|
18
18
|
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* `<figure>` receives the anchor ID for `{{formula:id}}` mentions.
|
|
8
8
|
*/
|
|
9
9
|
import { computed } from 'vue';
|
|
10
|
-
import type { Formula } from '
|
|
10
|
+
import type { Formula } from 'glossarist';
|
|
11
|
+
import type { FormulaNotation } from '../../adapters/non-verbal/types';
|
|
11
12
|
import { useNonVerbalEntity } from '../../composables/use-non-verbal-entity';
|
|
12
13
|
import { resolveFallbackChain } from '../../utils/locale';
|
|
13
14
|
import { anchorId } from '../../utils/non-verbal-anchor';
|
|
@@ -27,37 +28,38 @@ const k = () => 'formula' as const;
|
|
|
27
28
|
const { entity, state, error } = useNonVerbalEntity(k, () => props.datasetId, () => props.entityId);
|
|
28
29
|
|
|
29
30
|
const fallbackChain = computed(() => resolveFallbackChain(props.datasetLocales));
|
|
31
|
+
const form = computed(() => entity.value as Formula | null);
|
|
30
32
|
const anchor = computed(() => anchorId('formula', props.datasetId, props.entityId));
|
|
31
33
|
const descriptionId = computed(() => `${anchor.value}-desc`);
|
|
32
34
|
</script>
|
|
33
35
|
|
|
34
36
|
<template>
|
|
35
37
|
<figure
|
|
36
|
-
v-if="
|
|
38
|
+
v-if="form && state === 'loaded'"
|
|
37
39
|
:id="anchor"
|
|
38
40
|
class="formula-entity"
|
|
39
41
|
>
|
|
40
42
|
<div class="formula__expr-line">
|
|
41
43
|
<FormulaExpression
|
|
42
|
-
:expression="
|
|
43
|
-
:notation="(
|
|
44
|
+
:expression="form.expression"
|
|
45
|
+
:notation="(form.notation as FormulaNotation | null)"
|
|
44
46
|
:locale="locale"
|
|
45
47
|
:fallback-chain="fallbackChain"
|
|
46
48
|
/>
|
|
47
49
|
</div>
|
|
48
50
|
|
|
49
51
|
<NonVerbalCaption
|
|
50
|
-
:identifier="
|
|
51
|
-
:caption="
|
|
52
|
-
:description="
|
|
52
|
+
:identifier="form.identifier"
|
|
53
|
+
:caption="form.caption"
|
|
54
|
+
:description="form.description"
|
|
53
55
|
:locale="locale"
|
|
54
56
|
:fallback-chain="fallbackChain"
|
|
55
57
|
:description-id="descriptionId"
|
|
56
58
|
/>
|
|
57
59
|
|
|
58
60
|
<NonVerbalSources
|
|
59
|
-
v-if="
|
|
60
|
-
:sources="
|
|
61
|
+
v-if="form.sources?.length"
|
|
62
|
+
:sources="form.sources"
|
|
61
63
|
/>
|
|
62
64
|
</figure>
|
|
63
65
|
|
|
@@ -16,13 +16,13 @@ import { pickLocaleMap, localeToBcp47 } from '../../utils/locale';
|
|
|
16
16
|
import { loadPlurimath } from '../../utils/plurimath';
|
|
17
17
|
|
|
18
18
|
const props = defineProps<{
|
|
19
|
-
expression: LocalizedString;
|
|
20
|
-
notation: FormulaNotation;
|
|
19
|
+
expression: LocalizedString | null;
|
|
20
|
+
notation: FormulaNotation | null;
|
|
21
21
|
locale: string;
|
|
22
22
|
fallbackChain?: readonly string[];
|
|
23
23
|
}>();
|
|
24
24
|
|
|
25
|
-
const resolved = computed(() => pickLocaleMap(props.expression, props.locale, props.fallbackChain));
|
|
25
|
+
const resolved = computed(() => pickLocaleMap(props.expression ?? undefined, props.locale, props.fallbackChain));
|
|
26
26
|
const html = ref<string>('');
|
|
27
27
|
const lang = computed(() => resolved.value ? localeToBcp47(resolved.value.locale) : undefined);
|
|
28
28
|
|
|
@@ -34,7 +34,7 @@ const PLURIMATH_FORMAT: Record<FormulaNotation, string> = {
|
|
|
34
34
|
|
|
35
35
|
async function render() {
|
|
36
36
|
const r = resolved.value;
|
|
37
|
-
if (!r) { html.value = ''; return; }
|
|
37
|
+
if (!r || !props.notation) { html.value = ''; return; }
|
|
38
38
|
try {
|
|
39
39
|
const Plurimath = await loadPlurimath();
|
|
40
40
|
const p = new Plurimath(r.text, PLURIMATH_FORMAT[props.notation]);
|
|
@@ -22,20 +22,20 @@ import type { LocalizedString } from '../../adapters/non-verbal/types';
|
|
|
22
22
|
import { pickLocaleMap, localeToBcp47 } from '../../utils/locale';
|
|
23
23
|
|
|
24
24
|
const props = defineProps<{
|
|
25
|
-
identifier?: string;
|
|
26
|
-
caption?: LocalizedString;
|
|
27
|
-
description?: LocalizedString;
|
|
25
|
+
identifier?: string | null;
|
|
26
|
+
caption?: LocalizedString | null;
|
|
27
|
+
description?: LocalizedString | null;
|
|
28
28
|
locale: string;
|
|
29
29
|
fallbackChain?: readonly string[];
|
|
30
30
|
descriptionId?: string;
|
|
31
31
|
}>();
|
|
32
32
|
|
|
33
33
|
const captionResolved = computed(() =>
|
|
34
|
-
pickLocaleMap(props.caption, props.locale, props.fallbackChain),
|
|
34
|
+
pickLocaleMap(props.caption ?? undefined, props.locale, props.fallbackChain),
|
|
35
35
|
);
|
|
36
36
|
|
|
37
37
|
const descriptionResolved = computed(() =>
|
|
38
|
-
pickLocaleMap(props.description, props.locale, props.fallbackChain),
|
|
38
|
+
pickLocaleMap(props.description ?? undefined, props.locale, props.fallbackChain),
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
const captionLang = computed(() =>
|
|
@@ -6,20 +6,12 @@
|
|
|
6
6
|
* so the rendering matches the rest of the app. Each source may carry a
|
|
7
7
|
* modification note (e.g. "Adapted.") which is rendered alongside.
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
10
|
-
import type { NonVerbalSource } from '../../adapters/non-verbal/types';
|
|
9
|
+
import type { ConceptSource } from 'glossarist';
|
|
11
10
|
import CitationDisplay from '../CitationDisplay.vue';
|
|
12
11
|
|
|
13
12
|
defineProps<{
|
|
14
|
-
sources:
|
|
13
|
+
sources: ConceptSource[];
|
|
15
14
|
}>();
|
|
16
|
-
|
|
17
|
-
// CitationDisplay expects glossarist's Citation class. NonVerbalSource.origin
|
|
18
|
-
// is wire-compatible at runtime but typed differently on the consumer side;
|
|
19
|
-
// cast once at this boundary.
|
|
20
|
-
function asCitation(origin: NonVerbalSource['origin']): Citation | null {
|
|
21
|
-
return (origin as unknown as Citation) ?? null;
|
|
22
|
-
}
|
|
23
15
|
</script>
|
|
24
16
|
|
|
25
17
|
<template>
|
|
@@ -27,7 +19,7 @@ function asCitation(origin: NonVerbalSource['origin']): Citation | null {
|
|
|
27
19
|
<div class="nv-sources__label">Sources</div>
|
|
28
20
|
<ol class="nv-sources__list">
|
|
29
21
|
<li v-for="(src, i) in sources" :key="i" class="nv-source">
|
|
30
|
-
<CitationDisplay v-if="
|
|
22
|
+
<CitationDisplay v-if="src.origin" :citation="src.origin" />
|
|
31
23
|
<span v-if="src.modification" class="nv-source__modification">
|
|
32
24
|
— {{ src.modification }}
|
|
33
25
|
</span>
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* TableMarkup (HTML / Markdown / AsciiDoc) based on `content.kind`.
|
|
7
7
|
*/
|
|
8
8
|
import { computed } from 'vue';
|
|
9
|
-
import type { Table } from '
|
|
9
|
+
import type { Table } from 'glossarist';
|
|
10
|
+
import type { TableContent, TableFormat } from '../../adapters/non-verbal/types';
|
|
10
11
|
import { useNonVerbalEntity } from '../../composables/use-non-verbal-entity';
|
|
11
12
|
import { resolveFallbackChain } from '../../utils/locale';
|
|
12
13
|
import { anchorId } from '../../utils/non-verbal-anchor';
|
|
@@ -31,12 +32,13 @@ const anchor = computed(() => anchorId('table', props.datasetId, props.entityId)
|
|
|
31
32
|
const descriptionId = computed(() => `${anchor.value}-desc`);
|
|
32
33
|
|
|
33
34
|
const table = computed<Table | null>(() => entity.value as Table | null);
|
|
35
|
+
const content = computed<TableContent | null>(() => (table.value?.content ?? null) as TableContent | null);
|
|
34
36
|
const structuredContent = computed(() => {
|
|
35
|
-
const c =
|
|
37
|
+
const c = content.value;
|
|
36
38
|
return c && c.kind === 'structured' ? c : null;
|
|
37
39
|
});
|
|
38
40
|
const markup = computed(() => {
|
|
39
|
-
const c =
|
|
41
|
+
const c = content.value;
|
|
40
42
|
return c && c.kind === 'markup' ? c.markup : null;
|
|
41
43
|
});
|
|
42
44
|
</script>
|
|
@@ -56,7 +58,7 @@ const markup = computed(() => {
|
|
|
56
58
|
<TableMarkup
|
|
57
59
|
v-else-if="markup"
|
|
58
60
|
:content="markup"
|
|
59
|
-
:format="table.format"
|
|
61
|
+
:format="(table.format as TableFormat | null)"
|
|
60
62
|
:locale="locale"
|
|
61
63
|
:fallback-chain="fallbackChain"
|
|
62
64
|
/>
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { ref, watch, shallowRef } from 'vue';
|
|
10
10
|
import { getFactory } from '../adapters/factory';
|
|
11
|
-
import type {
|
|
11
|
+
import type { NonVerbalKind } from '../adapters/non-verbal/types';
|
|
12
|
+
import type { NonVerbalEntity } from 'glossarist';
|
|
12
13
|
|
|
13
14
|
export type LoadState = 'loading' | 'loaded' | 'not-found' | 'error';
|
|
14
15
|
|