@erudit-js/prose 4.2.0 → 4.3.0
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/dist/app/composables/context.d.ts +2 -0
- package/dist/app/composables/language.d.ts +2 -4
- package/dist/app/composables/language.js +6 -2
- package/dist/app/shared/block/Block.vue +5 -0
- package/dist/coreElement.d.ts +2 -7
- package/dist/elements/diagram/Diagram.vue +6 -4
- package/dist/elements/diagram/core.d.ts +3 -0
- package/dist/elements/diagram/core.js +4 -1
- package/dist/elements/diagram/icon.svg +2 -2
- package/dist/elements/math/components/InlinerMath.vue +18 -7
- package/dist/elements/math/core.d.ts +4 -2
- package/dist/elements/math/inliner.d.ts +12 -4
- package/dist/elements/math/inliner.js +6 -3
- package/dist/elements/problem/_global.d.ts +22 -0
- package/dist/elements/problem/components/ProblemContent.vue +10 -1
- package/dist/elements/problem/components/Problems.vue +9 -2
- package/dist/elements/problem/components/expanders/Check.vue +37 -18
- package/dist/elements/problem/core.d.ts +1 -0
- package/dist/elements/problem/problemCheck.d.ts +15 -8
- package/dist/elements/problem/problemCheck.js +65 -22
- package/dist/elements/problem/problems.d.ts +3 -0
- package/dist/elements/problem/problems.js +3 -0
- package/dist/elements/table/Table.vue +26 -19
- package/dist/elements/table/core.d.ts +21 -3
- package/dist/elements/table/core.js +13 -1
- package/dist/elements/table/icon.svg +1 -1
- package/dist/elements/video/icon.svg +1 -1
- package/dist/snippet.d.ts +1 -0
- package/dist/snippet.js +10 -5
- package/package.json +9 -8
|
@@ -3,6 +3,7 @@ import type { useFloating, UseFloatingOptions } from '@floating-ui/vue';
|
|
|
3
3
|
import type { EruditLanguageCode } from '@erudit-js/core/eruditConfig/language';
|
|
4
4
|
import type { EruditMode } from '@erudit-js/core/mode';
|
|
5
5
|
import type { FormatText } from '@erudit-js/core/formatText';
|
|
6
|
+
import type { ProblemCheckers } from '@erudit-js/core/problemCheck';
|
|
6
7
|
import type { ProseAppElement } from '../appElement.js';
|
|
7
8
|
export interface ProseContext {
|
|
8
9
|
mode: EruditMode;
|
|
@@ -14,6 +15,7 @@ export interface ProseContext {
|
|
|
14
15
|
baseUrl: string;
|
|
15
16
|
hashUrl: Ref<string | undefined>;
|
|
16
17
|
eruditIcons: Record<string, string>;
|
|
18
|
+
problemCheckers: ProblemCheckers;
|
|
17
19
|
EruditTransition: Component;
|
|
18
20
|
EruditIcon: Component;
|
|
19
21
|
EruditLink: Component;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { ProseElement } from 'tsprose';
|
|
2
2
|
import type { ElementPhrases } from '../language/element.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
copied: string;
|
|
6
|
-
}>;
|
|
3
|
+
import { type ProsePhrases } from '../language/prose.js';
|
|
4
|
+
export declare function useProseLanguage(): Promise<ProsePhrases>;
|
|
7
5
|
export declare function useElementPhrase<TPhrases extends ElementPhrases>(schemaName: string): Promise<TPhrases>;
|
|
8
6
|
export declare function useElementPhrase<TPhrases extends ElementPhrases>(element: ProseElement): Promise<TPhrases>;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { proseLanguages } from "../language/prose.js";
|
|
2
2
|
import { useProseContext } from "./context.js";
|
|
3
3
|
import { useAppElement } from "./appElement.js";
|
|
4
|
-
|
|
4
|
+
const proseLanguageCache = new Map();
|
|
5
|
+
export function useProseLanguage() {
|
|
5
6
|
const { languageCode } = useProseContext();
|
|
6
|
-
|
|
7
|
+
if (!proseLanguageCache.has(languageCode)) {
|
|
8
|
+
proseLanguageCache.set(languageCode, proseLanguages[languageCode]().then((m) => m.default));
|
|
9
|
+
}
|
|
10
|
+
return proseLanguageCache.get(languageCode);
|
|
7
11
|
}
|
|
8
12
|
export async function useElementPhrase(elementOrName) {
|
|
9
13
|
const { languageCode } = useProseContext();
|
|
@@ -11,6 +11,7 @@ import { useFloating, autoUpdate, offset, shift } from '@floating-ui/vue';
|
|
|
11
11
|
|
|
12
12
|
import { useProseContext } from '../../composables/context.js';
|
|
13
13
|
import { useElementIcon } from '../../composables/elementIcon.js';
|
|
14
|
+
import { useProseLanguage } from '../../composables/language.js';
|
|
14
15
|
import {
|
|
15
16
|
useIsAnchor,
|
|
16
17
|
useJumpToAnchor,
|
|
@@ -22,6 +23,10 @@ import type { BlockProseElement } from 'tsprose';
|
|
|
22
23
|
|
|
23
24
|
const { element } = defineProps<{ element: BlockProseElement }>();
|
|
24
25
|
const { EruditIcon, EruditTransition, setHtmlIds } = useProseContext();
|
|
26
|
+
|
|
27
|
+
// Pre-warm the prose language module so it is cached before the aside menu opens.
|
|
28
|
+
const _proseLang = useProseLanguage();
|
|
29
|
+
|
|
25
30
|
const elementIcon = await useElementIcon(element);
|
|
26
31
|
|
|
27
32
|
const formatTextState = useFormatTextState();
|
package/dist/coreElement.d.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import type { ElementStorageCreator, Schema } from 'tsprose';
|
|
2
|
+
import type { EruditDependencies } from '@erudit-js/core/dependencies';
|
|
2
3
|
import type { RawToProseSchemaHook } from './rawToProse/hook.js';
|
|
3
4
|
import type { EruditTag, ToEruditTag } from './tag.js';
|
|
4
|
-
export interface ProseCoreElementDependencies {
|
|
5
|
-
[dependencyName: string]: {
|
|
6
|
-
transpile?: boolean;
|
|
7
|
-
optimize?: boolean;
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
5
|
export type ToProseCoreElement<TSchema extends Schema, Tags extends EruditTag[] | undefined> = {
|
|
11
6
|
schema: TSchema;
|
|
12
7
|
tags: Tags;
|
|
13
|
-
dependencies?:
|
|
8
|
+
dependencies?: EruditDependencies;
|
|
14
9
|
} & ({
|
|
15
10
|
createStorage: ElementStorageCreator<TSchema>;
|
|
16
11
|
} | {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
useTemplateRef,
|
|
11
11
|
} from 'vue';
|
|
12
12
|
import type { ToProseElement } from 'tsprose';
|
|
13
|
+
import type { Mermaid } from 'mermaid';
|
|
13
14
|
|
|
14
15
|
import type { DiagramSchema } from './core.js';
|
|
15
16
|
import { usePhotoSwipe } from '../../app/shared/photoswipe/composable.js';
|
|
@@ -183,8 +184,9 @@ async function diagramIntersectHit(entries: IntersectionObserverEntry[]) {
|
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
async function renderDiagram() {
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
// @ts-expect-error
|
|
188
|
+
const mermaid = (await import('mermaid/dist/mermaid.esm.min'))
|
|
189
|
+
.default as Mermaid;
|
|
188
190
|
|
|
189
191
|
mermaid.initialize({
|
|
190
192
|
startOnLoad: false,
|
|
@@ -192,8 +194,8 @@ async function renderDiagram() {
|
|
|
192
194
|
markdownAutoWrap: false,
|
|
193
195
|
flowchart: {
|
|
194
196
|
useMaxWidth: true,
|
|
195
|
-
padding:
|
|
196
|
-
wrappingWidth:
|
|
197
|
+
padding: 16,
|
|
198
|
+
wrappingWidth: 600,
|
|
197
199
|
},
|
|
198
200
|
theme: 'base',
|
|
199
201
|
});
|
|
@@ -14,6 +14,9 @@ declare const _default: {
|
|
|
14
14
|
readonly schema: DiagramSchema;
|
|
15
15
|
readonly tags: [import("../../tag.js").ToEruditTag<DiagramSchema, "Diagram", unknown>];
|
|
16
16
|
readonly dependencies: {
|
|
17
|
+
readonly mermaid: {
|
|
18
|
+
transpile: true;
|
|
19
|
+
};
|
|
17
20
|
readonly photoswipe: {
|
|
18
21
|
optimize: boolean;
|
|
19
22
|
transpile: boolean;
|
|
@@ -23,5 +23,8 @@ export const Diagram = defineEruditTag({
|
|
|
23
23
|
export default defineProseCoreElement({
|
|
24
24
|
schema: diagramSchema,
|
|
25
25
|
tags: [Diagram],
|
|
26
|
-
dependencies: {
|
|
26
|
+
dependencies: {
|
|
27
|
+
...photoswipeDependency,
|
|
28
|
+
...{ mermaid: { transpile: true } }
|
|
29
|
+
}
|
|
27
30
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0
|
|
2
|
-
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 960">
|
|
2
|
+
<path d="M480.31,873.58c-39.36,0-72.62-13.59-99.8-40.76-27.18-27.18-40.76-60.44-40.76-99.8,0-32.49,9.68-61.07,29.05-85.74,19.37-24.68,44.04-41.39,74.03-50.13v-79.65h-149.94c-20.62,0-38.26-7.34-52.95-22.02s-22.02-32.33-22.02-52.95v-74.97h-56.23c-10.62,0-19.52-3.59-26.71-10.78s-10.78-16.09-10.78-26.71v-187.42c0-10.62,3.59-19.52,10.78-26.71s16.09-10.78,26.71-10.78h187.42c10.62,0,19.52,3.59,26.71,10.78s10.78,16.09,10.78,26.71v187.42c0,10.62-3.59,19.52-10.78,26.71s-16.09,10.78-26.71,10.78h-56.23v74.97h374.84v-79.65c-29.99-8.75-54.66-25.46-74.03-50.13s-29.05-53.26-29.05-85.74c0-39.36,13.59-72.62,40.76-99.8,27.18-27.18,60.44-40.76,99.8-40.76s72.62,13.59,99.8,40.76,40.76,60.44,40.76,99.8c0,32.49-9.68,61.07-29.05,85.74s-44.04,41.39-74.03,50.13v79.65c0,20.62-7.34,38.26-22.02,52.95s-32.33,22.02-52.95,22.02h-149.94v79.65c29.99,8.75,54.66,25.46,74.03,50.13,19.37,24.68,29.05,53.26,29.05,85.74,0,39.36-13.59,72.62-40.76,99.8-27.18,27.18-60.44,40.76-99.8,40.76Z"/>
|
|
3
3
|
</svg>
|
|
@@ -16,13 +16,19 @@ const { element } = defineProps<{
|
|
|
16
16
|
|
|
17
17
|
const inlinerMathStorage =
|
|
18
18
|
(await useElementStorage(element)) ??
|
|
19
|
-
(await createInlinerMathStorage(element.data));
|
|
19
|
+
(await createInlinerMathStorage(element.data.katex));
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
22
|
<template>
|
|
23
23
|
<Inliner :element>
|
|
24
24
|
<template v-if="inlinerMathStorage.type === 'text'">
|
|
25
|
-
<span
|
|
25
|
+
<span
|
|
26
|
+
:class="[
|
|
27
|
+
$style.inlinerMath,
|
|
28
|
+
$style.textMath,
|
|
29
|
+
element.data.currentColor && $style.currentColor,
|
|
30
|
+
]"
|
|
31
|
+
>
|
|
26
32
|
<template v-for="token of inlinerMathStorage.tokens">
|
|
27
33
|
<span :class="{ [$style.word]: token.type === 'word' }">
|
|
28
34
|
{{ token.value }}
|
|
@@ -32,7 +38,10 @@ const inlinerMathStorage =
|
|
|
32
38
|
</template>
|
|
33
39
|
<Katex
|
|
34
40
|
v-else
|
|
35
|
-
:class="
|
|
41
|
+
:class="[
|
|
42
|
+
$style.inlinerMath,
|
|
43
|
+
element.data.currentColor && $style.currentColor,
|
|
44
|
+
]"
|
|
36
45
|
:math="inlinerMathStorage.mathHtml"
|
|
37
46
|
mode="inline"
|
|
38
47
|
:freeze="false"
|
|
@@ -42,10 +51,12 @@ const inlinerMathStorage =
|
|
|
42
51
|
|
|
43
52
|
<style module>
|
|
44
53
|
.inlinerMath {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
&:not(.currentColor) {
|
|
55
|
+
--katex-color_default: light-dark(
|
|
56
|
+
color-mix(in hsl, var(--color-text-muted), var(--color-brand) 35%),
|
|
57
|
+
color-mix(in hsl, var(--color-text), var(--color-brand) 30%)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
.textMath {
|
|
@@ -19,11 +19,13 @@ declare const _default: readonly [{
|
|
|
19
19
|
};
|
|
20
20
|
}, {
|
|
21
21
|
readonly schema: import("./inliner.js").InlinerMathSchema;
|
|
22
|
-
readonly tags: [import("../../tag.js").ToEruditTag<import("./inliner.js").InlinerMathSchema, "M",
|
|
22
|
+
readonly tags: [import("../../tag.js").ToEruditTag<import("./inliner.js").InlinerMathSchema, "M", {
|
|
23
|
+
currentColor?: true;
|
|
24
|
+
} & import("tsprose").RequiredChildren>];
|
|
23
25
|
readonly createStorage: (element: {
|
|
24
26
|
schema: import("./inliner.js").InlinerMathSchema;
|
|
25
27
|
id: string;
|
|
26
|
-
data:
|
|
28
|
+
data: import("./inliner.js").InlinerMathData;
|
|
27
29
|
storageKey: string;
|
|
28
30
|
children: undefined;
|
|
29
31
|
__TSPROSE_proseElement: true;
|
|
@@ -24,19 +24,27 @@ export interface InlinerMathSchema extends Schema {
|
|
|
24
24
|
name: 'inlinerMath';
|
|
25
25
|
type: 'inliner';
|
|
26
26
|
linkable: true;
|
|
27
|
-
Data:
|
|
27
|
+
Data: InlinerMathData;
|
|
28
28
|
Storage: InlinerMathStorage;
|
|
29
29
|
Children: undefined;
|
|
30
30
|
}
|
|
31
|
+
export interface InlinerMathData {
|
|
32
|
+
katex: string;
|
|
33
|
+
currentColor?: boolean;
|
|
34
|
+
}
|
|
31
35
|
export declare const inlinerMathSchema: InlinerMathSchema;
|
|
32
|
-
export declare const M: import("../../tag.js").ToEruditTag<InlinerMathSchema, "M",
|
|
36
|
+
export declare const M: import("../../tag.js").ToEruditTag<InlinerMathSchema, "M", {
|
|
37
|
+
currentColor?: true;
|
|
38
|
+
} & RequiredChildren>;
|
|
33
39
|
export declare const inlinerMathCoreElement: {
|
|
34
40
|
readonly schema: InlinerMathSchema;
|
|
35
|
-
readonly tags: [import("../../tag.js").ToEruditTag<InlinerMathSchema, "M",
|
|
41
|
+
readonly tags: [import("../../tag.js").ToEruditTag<InlinerMathSchema, "M", {
|
|
42
|
+
currentColor?: true;
|
|
43
|
+
} & RequiredChildren>];
|
|
36
44
|
readonly createStorage: (element: {
|
|
37
45
|
schema: InlinerMathSchema;
|
|
38
46
|
id: string;
|
|
39
|
-
data:
|
|
47
|
+
data: InlinerMathData;
|
|
40
48
|
storageKey: string;
|
|
41
49
|
children: undefined;
|
|
42
50
|
__TSPROSE_proseElement: true;
|
|
@@ -50,19 +50,22 @@ export const inlinerMathSchema = defineSchema({
|
|
|
50
50
|
export const M = defineEruditTag({
|
|
51
51
|
tagName: "M",
|
|
52
52
|
schema: inlinerMathSchema
|
|
53
|
-
})(({ element, tagName, children }) => {
|
|
53
|
+
})(({ props, element, tagName, children }) => {
|
|
54
54
|
ensureTagChildren(tagName, children, textSchema);
|
|
55
55
|
const katex = normalizeKatex(children[0].data);
|
|
56
56
|
if (!katex) {
|
|
57
57
|
throw new EruditProseError(`<${tagName}> tag must contain non-empty KaTeX math expression.`);
|
|
58
58
|
}
|
|
59
|
-
element.data = katex;
|
|
59
|
+
element.data = { katex };
|
|
60
60
|
element.storageKey = `$ ${katex} $`;
|
|
61
|
+
if (props.currentColor) {
|
|
62
|
+
element.data.currentColor = true;
|
|
63
|
+
}
|
|
61
64
|
});
|
|
62
65
|
export const inlinerMathCoreElement = defineProseCoreElement({
|
|
63
66
|
schema: inlinerMathSchema,
|
|
64
67
|
tags: [M],
|
|
65
|
-
createStorage: async (element) => createInlinerMathStorage(element.data),
|
|
68
|
+
createStorage: async (element) => createInlinerMathStorage(element.data.katex),
|
|
66
69
|
dependencies: katexDependency
|
|
67
70
|
});
|
|
68
71
|
export async function createInlinerMathStorage(katex) {
|
|
@@ -231,6 +231,12 @@ export const Problems = '_tag_';
|
|
|
231
231
|
* - `<ProblemCheck>` (repeatable)
|
|
232
232
|
* - `<ProblemNote>`
|
|
233
233
|
*
|
|
234
|
+
* ### Props
|
|
235
|
+
* - `label` — optional tab label string
|
|
236
|
+
* - `standalone` — when set, hides any shared description blocks defined at the
|
|
237
|
+
* `<Problems>` level for this sub-problem only. Useful when a general task
|
|
238
|
+
* applies to all sub-problems except certain ones.
|
|
239
|
+
*
|
|
234
240
|
* @title SubProblem
|
|
235
241
|
* @layout block
|
|
236
242
|
* @example
|
|
@@ -243,6 +249,22 @@ export const Problems = '_tag_';
|
|
|
243
249
|
* <ProblemCheck answer={42} />
|
|
244
250
|
* </SubProblem>
|
|
245
251
|
* ```
|
|
252
|
+
* @example
|
|
253
|
+
* ```tsx
|
|
254
|
+
* <Problems title="Problem Set">
|
|
255
|
+
* <P>Find all roots.</P>
|
|
256
|
+
* <SubProblem>
|
|
257
|
+
* <ProblemDescription>
|
|
258
|
+
* This text is visible along with the "Find all roots" description since standalone is not set.
|
|
259
|
+
* </ProblemDescription>
|
|
260
|
+
* </SubProblem>
|
|
261
|
+
* <SubProblem standalone>
|
|
262
|
+
* <ProblemDescription>
|
|
263
|
+
* Custom problem description. No "Find all roots" text shown since standalone is set.
|
|
264
|
+
* </ProblemDescription>
|
|
265
|
+
* </SubProblem>
|
|
266
|
+
* </Problems>
|
|
267
|
+
* ```
|
|
246
268
|
*/
|
|
247
269
|
export const SubProblem = '_tag_';
|
|
248
270
|
|
|
@@ -189,6 +189,13 @@ onMounted(async () => {
|
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
function stripIds(el: ToProseElement<any>): void {
|
|
193
|
+
delete (el as any).id;
|
|
194
|
+
for (const child of (el.children ?? []) as ToProseElement<any>[]) {
|
|
195
|
+
stripIds(child);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
192
199
|
let currentSeed: ProblemSeed = DEFAULT_SEED;
|
|
193
200
|
async function doGenerate(seed: ProblemSeed) {
|
|
194
201
|
if (!scriptInstance.value) {
|
|
@@ -207,7 +214,9 @@ async function doGenerate(seed: ProblemSeed) {
|
|
|
207
214
|
const proseElements: ToProseElement<ProblemContentChild>[] = [];
|
|
208
215
|
for (const rawElement of rawElements) {
|
|
209
216
|
const resolveResult = await eruditRawToProse({ rawProse: rawElement });
|
|
210
|
-
|
|
217
|
+
const prose = resolveResult.prose as ToProseElement<ProblemContentChild>;
|
|
218
|
+
stripIds(prose);
|
|
219
|
+
proseElements.push(prose);
|
|
211
220
|
}
|
|
212
221
|
|
|
213
222
|
if (currentSeed !== seed) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ref, watchEffect } from 'vue';
|
|
2
|
+
import { ref, computed, watchEffect } from 'vue';
|
|
3
3
|
import {
|
|
4
4
|
isProseElement,
|
|
5
5
|
type BlockProseElement,
|
|
@@ -36,6 +36,10 @@ const subProblems = element.children.filter((child) =>
|
|
|
36
36
|
|
|
37
37
|
const activeSubProblemI = ref(0);
|
|
38
38
|
|
|
39
|
+
const showShared = computed(
|
|
40
|
+
() => subProblems[activeSubProblemI.value]?.data.standalone !== true,
|
|
41
|
+
);
|
|
42
|
+
|
|
39
43
|
function getUnlabeledOrdinal(index: number) {
|
|
40
44
|
let count = 0;
|
|
41
45
|
for (let i = 0; i <= index; i++) {
|
|
@@ -57,7 +61,10 @@ watchEffect(() => {
|
|
|
57
61
|
<Block :element>
|
|
58
62
|
<ProblemContainer>
|
|
59
63
|
<ProblemHeader :info="element.data" />
|
|
60
|
-
<div
|
|
64
|
+
<div
|
|
65
|
+
v-if="sharedChildren.length && showShared"
|
|
66
|
+
class="pt-(--proseAsideWidth)"
|
|
67
|
+
>
|
|
61
68
|
<Render v-for="sharedChild of sharedChildren" :element="sharedChild" />
|
|
62
69
|
</div>
|
|
63
70
|
<div
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
checkProblemAnswer,
|
|
7
7
|
fromSerializableValidator,
|
|
8
8
|
type ProblemCheckSchema,
|
|
9
|
+
type ProblemCheckContext,
|
|
9
10
|
} from '../../problemCheck.js';
|
|
10
11
|
import checkIcon from '../../assets/actions/check.svg?raw';
|
|
11
12
|
import plusIcon from '../../../../app/shared/assets/plus.svg?raw';
|
|
@@ -14,7 +15,7 @@ import { useFormatText } from '../../../../app/composables/formatText.js';
|
|
|
14
15
|
import { useProseContext } from '../../../../app/index.js';
|
|
15
16
|
import { useProblemPhrase } from '../../composables/phrase.js';
|
|
16
17
|
|
|
17
|
-
type CheckStatus = 'default' | 'correct' | 'wrong';
|
|
18
|
+
type CheckStatus = 'default' | 'loading' | 'correct' | 'wrong';
|
|
18
19
|
|
|
19
20
|
type CheckState = {
|
|
20
21
|
icon: string;
|
|
@@ -40,6 +41,13 @@ const emit = defineEmits<{
|
|
|
40
41
|
(event: 'status-change', status: CheckStatus): void;
|
|
41
42
|
}>();
|
|
42
43
|
|
|
44
|
+
const state = ref<CheckStatus>('default');
|
|
45
|
+
|
|
46
|
+
const formatText = useFormatText();
|
|
47
|
+
const { EruditIcon, EruditTransition, loadingSvg, problemCheckers } =
|
|
48
|
+
useProseContext();
|
|
49
|
+
const phrase = await useProblemPhrase();
|
|
50
|
+
|
|
43
51
|
const states: Record<CheckStatus, CheckState> = {
|
|
44
52
|
default: {
|
|
45
53
|
icon: checkIcon,
|
|
@@ -48,6 +56,12 @@ const states: Record<CheckStatus, CheckState> = {
|
|
|
48
56
|
buttonClass:
|
|
49
57
|
'text-text-muted border-border hocus:border-text-muted hocus:text-text',
|
|
50
58
|
},
|
|
59
|
+
loading: {
|
|
60
|
+
icon: loadingSvg,
|
|
61
|
+
labelClass: 'text-text-muted',
|
|
62
|
+
inputClass: 'border-border text-text',
|
|
63
|
+
buttonClass: 'text-text-muted border-border',
|
|
64
|
+
},
|
|
51
65
|
correct: {
|
|
52
66
|
icon: successIcon,
|
|
53
67
|
labelClass: 'text-lime-600',
|
|
@@ -63,11 +77,8 @@ const states: Record<CheckStatus, CheckState> = {
|
|
|
63
77
|
},
|
|
64
78
|
};
|
|
65
79
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
const formatText = useFormatText();
|
|
69
|
-
const { EruditIcon, EruditTransition } = useProseContext();
|
|
70
|
-
const phrase = await useProblemPhrase();
|
|
80
|
+
const currentState = computed(() => states[state.value]);
|
|
81
|
+
const isLoading = computed(() => state.value === 'loading');
|
|
71
82
|
|
|
72
83
|
const hint = computed(() => {
|
|
73
84
|
if (check.data.hint) {
|
|
@@ -109,7 +120,7 @@ watch(answerInput, () => {
|
|
|
109
120
|
emit('status-change', 'default');
|
|
110
121
|
});
|
|
111
122
|
|
|
112
|
-
function doCheck() {
|
|
123
|
+
async function doCheck() {
|
|
113
124
|
const newInput = answerInput.value.replace(/\s+/g, ' ').trim();
|
|
114
125
|
|
|
115
126
|
if (newInput === lastCheckedInput.value) {
|
|
@@ -117,6 +128,7 @@ function doCheck() {
|
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
lastCheckedInput.value = newInput;
|
|
131
|
+
state.value = 'loading';
|
|
120
132
|
|
|
121
133
|
if (script) {
|
|
122
134
|
const ok = script.check(newInput || undefined);
|
|
@@ -125,12 +137,17 @@ function doCheck() {
|
|
|
125
137
|
return;
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
|
|
140
|
+
const checkContext: ProblemCheckContext = {
|
|
141
|
+
yesRegexp: phrase.yes_regexp,
|
|
142
|
+
noRegexp: phrase.no_regexp,
|
|
143
|
+
checkers: problemCheckers,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
state.value = (await checkProblemAnswer(
|
|
129
147
|
newInput,
|
|
130
|
-
phrase.yes_regexp,
|
|
131
|
-
phrase.no_regexp,
|
|
132
148
|
validator.value,
|
|
133
|
-
|
|
149
|
+
checkContext,
|
|
150
|
+
))
|
|
134
151
|
? 'correct'
|
|
135
152
|
: 'wrong';
|
|
136
153
|
|
|
@@ -143,7 +160,7 @@ function doCheck() {
|
|
|
143
160
|
<div
|
|
144
161
|
:class="[
|
|
145
162
|
'text-main-sm font-medium transition-[color]',
|
|
146
|
-
|
|
163
|
+
currentState.labelClass,
|
|
147
164
|
]"
|
|
148
165
|
>
|
|
149
166
|
<span @click="answerInputElement?.focus()">
|
|
@@ -157,36 +174,38 @@ function doCheck() {
|
|
|
157
174
|
v-model="answerInput"
|
|
158
175
|
type="text"
|
|
159
176
|
autocomplete="off"
|
|
177
|
+
:disabled="isLoading"
|
|
160
178
|
:placeholder="check.data.placeholder"
|
|
161
179
|
:class="[
|
|
162
180
|
`bg-bg-main text-main-sm focus:ring-brand relative z-10 min-w-0 flex-1
|
|
163
181
|
rounded rounded-tr-none rounded-br-none border border-r-0 px-2.5 py-1
|
|
164
182
|
ring-2 ring-transparent outline-0
|
|
165
183
|
transition-[border,color,background,box-shadow]`,
|
|
166
|
-
|
|
184
|
+
currentState.inputClass,
|
|
167
185
|
]"
|
|
168
186
|
/>
|
|
169
187
|
|
|
170
188
|
<button
|
|
171
189
|
type="submit"
|
|
190
|
+
:disabled="isLoading"
|
|
172
191
|
:class="[
|
|
173
192
|
`bg-bg-main relative w-[50px] cursor-pointer rounded rounded-tl-none
|
|
174
193
|
rounded-bl-none border outline-0 transition-[border,color,background]`,
|
|
175
|
-
|
|
194
|
+
currentState.buttonClass,
|
|
176
195
|
]"
|
|
177
196
|
>
|
|
178
197
|
<EruditIcon
|
|
179
198
|
:key="state"
|
|
180
|
-
:name="
|
|
181
|
-
:class="['invisible m-auto text-[1.2em]',
|
|
199
|
+
:name="currentState.icon"
|
|
200
|
+
:class="['invisible m-auto text-[1.2em]', currentState.iconClass]"
|
|
182
201
|
/>
|
|
183
202
|
<EruditTransition>
|
|
184
203
|
<EruditIcon
|
|
185
204
|
:key="state"
|
|
186
|
-
:name="
|
|
205
|
+
:name="currentState.icon"
|
|
187
206
|
:class="[
|
|
188
207
|
'absolute top-1/2 left-1/2 -translate-1/2 text-[1.2em]',
|
|
189
|
-
|
|
208
|
+
currentState.iconClass,
|
|
190
209
|
]"
|
|
191
210
|
/>
|
|
192
211
|
</EruditTransition>
|
|
@@ -18,6 +18,7 @@ declare const _default: readonly [{
|
|
|
18
18
|
readonly schema: import("./problems.js").SubProblemSchema;
|
|
19
19
|
readonly tags: [import("../../tag.js").ToEruditTag<import("./problems.js").SubProblemSchema, "SubProblem", {
|
|
20
20
|
label?: string;
|
|
21
|
+
standalone?: true;
|
|
21
22
|
} & ({
|
|
22
23
|
children: {};
|
|
23
24
|
script?: undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { XOR } from 'ts-xor';
|
|
2
2
|
import { type OptionalChildren, type Schema } from 'tsprose';
|
|
3
|
+
import { type ProblemCheckObject, type ProblemCheckers } from '@erudit-js/core/problemCheck';
|
|
3
4
|
export interface ProblemCheckInfo {
|
|
4
5
|
label?: string;
|
|
5
6
|
hint?: string;
|
|
@@ -62,11 +63,12 @@ export declare const problemCheckCoreElement: {
|
|
|
62
63
|
script: string;
|
|
63
64
|
}> & OptionalChildren)>];
|
|
64
65
|
};
|
|
66
|
+
export { type ProblemCheckObject, isProblemCheckObject, type ProblemCheckers, } from '@erudit-js/core/problemCheck';
|
|
65
67
|
export interface ProblemCheckValidatorBoolean {
|
|
66
68
|
type: 'boolean';
|
|
67
69
|
answer: boolean;
|
|
68
70
|
}
|
|
69
|
-
export type ProblemCheckValue = undefined | number | string | RegExp;
|
|
71
|
+
export type ProblemCheckValue = undefined | number | string | RegExp | ProblemCheckObject;
|
|
70
72
|
export type ProblemCheckValueDefined = Exclude<ProblemCheckValue, undefined>;
|
|
71
73
|
export interface ProblemCheckValidatorValue {
|
|
72
74
|
type: 'value';
|
|
@@ -83,13 +85,13 @@ export interface ProblemCheckValidatorScript {
|
|
|
83
85
|
name: string;
|
|
84
86
|
}
|
|
85
87
|
export type ProblemCheckValidator = ProblemCheckValidatorBoolean | ProblemCheckValidatorValue | ProblemCheckValidatorArray | ProblemCheckValidatorScript;
|
|
86
|
-
export declare function toSerializableValidator(validator: ProblemCheckValidator): ProblemCheckValidatorBoolean | ProblemCheckValidatorScript | {
|
|
88
|
+
export declare function toSerializableValidator(validator: ProblemCheckValidator): ProblemCheckValidatorBoolean | ProblemCheckValidatorValue | ProblemCheckValidatorArray | ProblemCheckValidatorScript | {
|
|
87
89
|
type: string;
|
|
88
|
-
answer: string | number | {
|
|
90
|
+
answer: string | number | ProblemCheckObject | {
|
|
89
91
|
readonly type: "regexp";
|
|
90
92
|
readonly source: string;
|
|
91
93
|
readonly flags: string;
|
|
92
|
-
} | (string | number | {
|
|
94
|
+
} | (string | number | ProblemCheckObject | {
|
|
93
95
|
readonly type: "regexp";
|
|
94
96
|
readonly source: string;
|
|
95
97
|
readonly flags: string;
|
|
@@ -101,16 +103,21 @@ export declare function toSerializableValidator(validator: ProblemCheckValidator
|
|
|
101
103
|
type: string;
|
|
102
104
|
ordered: boolean;
|
|
103
105
|
separator: string;
|
|
104
|
-
answers: (string | number | {
|
|
106
|
+
answers: (string | number | ProblemCheckObject | {
|
|
105
107
|
readonly type: "regexp";
|
|
106
108
|
readonly source: string;
|
|
107
109
|
readonly flags: string;
|
|
108
|
-
} | (string | number | {
|
|
110
|
+
} | (string | number | ProblemCheckObject | {
|
|
109
111
|
readonly type: "regexp";
|
|
110
112
|
readonly source: string;
|
|
111
113
|
readonly flags: string;
|
|
112
114
|
} | undefined)[] | undefined)[];
|
|
113
115
|
answer?: undefined;
|
|
114
116
|
};
|
|
115
|
-
export declare function fromSerializableValidator(serializedValidator: any): ProblemCheckValidator;
|
|
116
|
-
export
|
|
117
|
+
export declare function fromSerializableValidator(serializedValidator: any): ProblemCheckValidator | ProblemCheckObject;
|
|
118
|
+
export interface ProblemCheckContext {
|
|
119
|
+
yesRegexp: RegExp;
|
|
120
|
+
noRegexp: RegExp;
|
|
121
|
+
checkers: ProblemCheckers;
|
|
122
|
+
}
|
|
123
|
+
export declare function checkProblemAnswer(answer: string, against: ProblemCheckValidator | ProblemCheckObject, context: ProblemCheckContext): Promise<boolean>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineSchema, ensureTagChildren } from "tsprose";
|
|
2
|
+
import { isProblemCheckObject } from "@erudit-js/core/problemCheck";
|
|
2
3
|
import { defineEruditTag } from "../../tag.js";
|
|
3
4
|
import { defineProseCoreElement } from "../../coreElement.js";
|
|
4
5
|
import { EruditProseError } from "../../error.js";
|
|
@@ -46,9 +47,17 @@ export const ProblemCheck = defineEruditTag({
|
|
|
46
47
|
answer: !!props.boolean
|
|
47
48
|
};
|
|
48
49
|
} else if ("answer" in props) {
|
|
50
|
+
const answerProp = props.answer;
|
|
51
|
+
if (!Array.isArray(answerProp) && isProblemCheckObject(answerProp)) {
|
|
52
|
+
element.data = {
|
|
53
|
+
...checkInfo,
|
|
54
|
+
serializedValidator: answerProp
|
|
55
|
+
};
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
49
58
|
validator = {
|
|
50
59
|
type: "value",
|
|
51
|
-
answer:
|
|
60
|
+
answer: answerProp
|
|
52
61
|
};
|
|
53
62
|
} else if ("answers" in props) {
|
|
54
63
|
const answersProp = props.answers;
|
|
@@ -83,7 +92,14 @@ export const problemCheckCoreElement = defineProseCoreElement({
|
|
|
83
92
|
schema: problemCheckSchema,
|
|
84
93
|
tags: [ProblemCheck]
|
|
85
94
|
});
|
|
95
|
+
//
|
|
96
|
+
// ProblemCheck Data
|
|
97
|
+
//
|
|
98
|
+
export { isProblemCheckObject } from "@erudit-js/core/problemCheck";
|
|
86
99
|
export function toSerializableValidator(validator) {
|
|
100
|
+
if (validator.__ERUDIT_CHECK === true) {
|
|
101
|
+
return validator;
|
|
102
|
+
}
|
|
87
103
|
if (validator.type === "boolean") {
|
|
88
104
|
return validator;
|
|
89
105
|
}
|
|
@@ -122,6 +138,9 @@ export function toSerializableValidator(validator) {
|
|
|
122
138
|
throw new Error(`Unknown ProblemCheckData type "${validator.type}"!`);
|
|
123
139
|
}
|
|
124
140
|
export function fromSerializableValidator(serializedValidator) {
|
|
141
|
+
if (serializedValidator.__ERUDIT_CHECK === true) {
|
|
142
|
+
return serializedValidator;
|
|
143
|
+
}
|
|
125
144
|
if (serializedValidator.type === "boolean") {
|
|
126
145
|
return serializedValidator;
|
|
127
146
|
}
|
|
@@ -155,11 +174,27 @@ export function fromSerializableValidator(serializedValidator) {
|
|
|
155
174
|
}
|
|
156
175
|
throw new Error(`Unknown ProblemCheckData type "${serializedValidator.type}"!`);
|
|
157
176
|
}
|
|
158
|
-
export function checkProblemAnswer(answer,
|
|
159
|
-
if (
|
|
160
|
-
|
|
177
|
+
export async function checkProblemAnswer(answer, against, context) {
|
|
178
|
+
if (isProblemCheckObject(against)) {
|
|
179
|
+
const checker = context.checkers[against.name];
|
|
180
|
+
if (!checker) {
|
|
181
|
+
console.warn(`No problem checker found for "${against.name}"`);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return await checker.check(against.data, answer);
|
|
161
185
|
}
|
|
162
|
-
|
|
186
|
+
if (against.type === "boolean") {
|
|
187
|
+
return against.answer === true ? context.yesRegexp.test(answer) : context.noRegexp.test(answer);
|
|
188
|
+
}
|
|
189
|
+
const checkDefinedAnswer = async (expected, answer) => {
|
|
190
|
+
if (isProblemCheckObject(expected)) {
|
|
191
|
+
const checker = context.checkers[expected.name];
|
|
192
|
+
if (!checker) {
|
|
193
|
+
console.warn(`No problem checker found for "${expected.name}"`);
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return await checker.check(expected.data, answer);
|
|
197
|
+
}
|
|
163
198
|
if (typeof expected === "number") {
|
|
164
199
|
return Number(answer) === expected;
|
|
165
200
|
}
|
|
@@ -168,42 +203,50 @@ export function checkProblemAnswer(answer, yesRegexp, noRegexp, validator) {
|
|
|
168
203
|
}
|
|
169
204
|
return answer === String(expected);
|
|
170
205
|
};
|
|
171
|
-
const checkAnswer = (expect, answer) => {
|
|
206
|
+
const checkAnswer = async (expect, answer) => {
|
|
172
207
|
if (expect === undefined || expect === null) {
|
|
173
208
|
return answer.trim() === "";
|
|
174
209
|
}
|
|
175
210
|
return checkDefinedAnswer(expect, answer);
|
|
176
211
|
};
|
|
177
|
-
if (
|
|
178
|
-
const anyOf = Array.isArray(
|
|
179
|
-
|
|
212
|
+
if (against.type === "value") {
|
|
213
|
+
const anyOf = Array.isArray(against.answer) ? against.answer : [against.answer];
|
|
214
|
+
const results = await Promise.all(anyOf.map((expect) => checkAnswer(expect, answer)));
|
|
215
|
+
return results.some(Boolean);
|
|
180
216
|
}
|
|
181
|
-
if (
|
|
182
|
-
const separatorRegexp = new RegExp(`\\s*${
|
|
217
|
+
if (against.type === "array") {
|
|
218
|
+
const separatorRegexp = new RegExp(`\\s*${against.separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*`, "g");
|
|
183
219
|
const parts = answer.split(separatorRegexp).map((p) => p.trim());
|
|
184
|
-
const checkExpected = (expected, actual) => {
|
|
220
|
+
const checkExpected = async (expected, actual) => {
|
|
185
221
|
if (Array.isArray(expected)) {
|
|
186
|
-
|
|
187
|
-
return
|
|
222
|
+
const results = await Promise.all(expected.map((e) => checkDefinedAnswer(e, actual)));
|
|
223
|
+
return results.some(Boolean);
|
|
188
224
|
}
|
|
189
225
|
return checkDefinedAnswer(expected, actual);
|
|
190
226
|
};
|
|
191
|
-
if (parts.length !==
|
|
227
|
+
if (parts.length !== against.answers.length) {
|
|
192
228
|
return false;
|
|
193
229
|
}
|
|
194
|
-
if (
|
|
195
|
-
|
|
230
|
+
if (against.ordered) {
|
|
231
|
+
const results = await Promise.all(against.answers.map((expected, i) => checkExpected(expected, parts[i])));
|
|
232
|
+
return results.every(Boolean);
|
|
196
233
|
}
|
|
197
234
|
// unordered matching (multiset semantics)
|
|
198
235
|
const remaining = [...parts];
|
|
199
|
-
for (const expected of
|
|
200
|
-
|
|
201
|
-
|
|
236
|
+
for (const expected of against.answers) {
|
|
237
|
+
let foundIndex = -1;
|
|
238
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
239
|
+
if (await checkExpected(expected, remaining[i])) {
|
|
240
|
+
foundIndex = i;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (foundIndex === -1) {
|
|
202
245
|
return false;
|
|
203
246
|
}
|
|
204
|
-
remaining.splice(
|
|
247
|
+
remaining.splice(foundIndex, 1);
|
|
205
248
|
}
|
|
206
249
|
return true;
|
|
207
250
|
}
|
|
208
|
-
throw new EruditProseError(`"checkProblemAnswer" not implemented for type "${
|
|
251
|
+
throw new EruditProseError(`"checkProblemAnswer" not implemented for type "${against.type}"!`);
|
|
209
252
|
}
|
|
@@ -13,11 +13,13 @@ export interface SubProblemSchema extends Schema {
|
|
|
13
13
|
}
|
|
14
14
|
export interface SubProblemData {
|
|
15
15
|
label?: string;
|
|
16
|
+
standalone?: true;
|
|
16
17
|
scriptUniques?: Record<string, Unique>;
|
|
17
18
|
}
|
|
18
19
|
export declare const subProblemSchema: SubProblemSchema;
|
|
19
20
|
export declare const SubProblem: import("../../tag.js").ToEruditTag<SubProblemSchema, "SubProblem", {
|
|
20
21
|
label?: string;
|
|
22
|
+
standalone?: true;
|
|
21
23
|
} & ({
|
|
22
24
|
children: {};
|
|
23
25
|
script?: undefined;
|
|
@@ -29,6 +31,7 @@ export declare const subProblemCoreElement: {
|
|
|
29
31
|
readonly schema: SubProblemSchema;
|
|
30
32
|
readonly tags: [import("../../tag.js").ToEruditTag<SubProblemSchema, "SubProblem", {
|
|
31
33
|
label?: string;
|
|
34
|
+
standalone?: true;
|
|
32
35
|
} & ({
|
|
33
36
|
children: {};
|
|
34
37
|
script?: undefined;
|
|
@@ -20,6 +20,9 @@ export const SubProblem = defineEruditTag({
|
|
|
20
20
|
if (label) {
|
|
21
21
|
element.data.label = label;
|
|
22
22
|
}
|
|
23
|
+
if (props.standalone) {
|
|
24
|
+
element.data.standalone = true;
|
|
25
|
+
}
|
|
23
26
|
if (props.script && children) {
|
|
24
27
|
throw new EruditProseError(`<${tagName}> cannot have both script and children!`);
|
|
25
28
|
}
|
|
@@ -36,12 +36,22 @@ const rows = computed(() =>
|
|
|
36
36
|
<tr
|
|
37
37
|
v-for="row in rows"
|
|
38
38
|
:key="row.id"
|
|
39
|
-
class="odd:bg-(--oddCellBg) even:bg-(--evenCellBg)"
|
|
39
|
+
class="group odd:bg-(--oddCellBg) even:bg-(--evenCellBg)"
|
|
40
40
|
>
|
|
41
41
|
<td
|
|
42
42
|
v-for="cell in row.children"
|
|
43
43
|
:key="cell.id"
|
|
44
|
-
class="
|
|
44
|
+
:class="[
|
|
45
|
+
`py-small px-normal group-hocus:inset-ring-(--tableBorder)
|
|
46
|
+
rounded inset-ring-2 inset-ring-transparent
|
|
47
|
+
transition-[box-shadow]`,
|
|
48
|
+
cell.data?.center
|
|
49
|
+
? 'text-center'
|
|
50
|
+
: cell.data?.right
|
|
51
|
+
? 'text-right'
|
|
52
|
+
: '',
|
|
53
|
+
cell.data?.freeze && 'whitespace-nowrap',
|
|
54
|
+
]"
|
|
45
55
|
>
|
|
46
56
|
<Render
|
|
47
57
|
v-for="inliner of cell.children"
|
|
@@ -59,41 +69,38 @@ const rows = computed(() =>
|
|
|
59
69
|
|
|
60
70
|
<style module>
|
|
61
71
|
.table {
|
|
62
|
-
--tableBorder:
|
|
63
|
-
in
|
|
64
|
-
var(--color-brand),
|
|
65
|
-
var(--color-border) 85%
|
|
72
|
+
--tableBorder: light-dark(
|
|
73
|
+
color-mix(in hsl, var(--color-brand), var(--color-border) 70%),
|
|
74
|
+
color-mix(in hsl, var(--color-brand), var(--color-border) 85%)
|
|
66
75
|
);
|
|
67
76
|
|
|
68
|
-
--evenCellBg:
|
|
69
|
-
in
|
|
70
|
-
|
|
71
|
-
var(--color-brand) 3%
|
|
77
|
+
--evenCellBg: light-dark(
|
|
78
|
+
color-mix(in hsl, #f5f5f5, var(--color-brand) 12%),
|
|
79
|
+
color-mix(in hsl, #2b2b2b, var(--color-brand) 4%)
|
|
72
80
|
);
|
|
73
81
|
|
|
74
|
-
--oddCellBg:
|
|
75
|
-
in
|
|
76
|
-
|
|
77
|
-
var(--color-brand) 10%
|
|
82
|
+
--oddCellBg: light-dark(
|
|
83
|
+
color-mix(in hsl, #f5f5f5, var(--color-brand) 18%),
|
|
84
|
+
color-mix(in hsl, #2b2b2b, var(--color-brand) 8%)
|
|
78
85
|
);
|
|
79
86
|
|
|
80
87
|
[data-prose-accent] & {
|
|
81
88
|
--tableBorder: color-mix(
|
|
82
89
|
in srgb,
|
|
83
|
-
var(--
|
|
84
|
-
var(--color-border)
|
|
90
|
+
var(--accentText),
|
|
91
|
+
var(--color-border) 60%
|
|
85
92
|
);
|
|
86
93
|
|
|
87
94
|
--evenCellBg: color-mix(
|
|
88
95
|
in srgb,
|
|
89
|
-
light-dark(var(--
|
|
96
|
+
light-dark(color-mix(in srgb, #fff, var(--accentText) 13%), #252525),
|
|
90
97
|
var(--accentText) 12%
|
|
91
98
|
);
|
|
92
99
|
|
|
93
100
|
--oddCellBg: color-mix(
|
|
94
101
|
in srgb,
|
|
95
|
-
light-dark(var(--
|
|
96
|
-
var(--accentText)
|
|
102
|
+
light-dark(color-mix(in srgb, #fff, var(--accentText) 13%), #252525),
|
|
103
|
+
var(--accentText) 19%
|
|
97
104
|
);
|
|
98
105
|
}
|
|
99
106
|
}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { type InlinerSchema, type Schema } from 'tsprose';
|
|
2
|
+
import type { XOR } from 'ts-xor';
|
|
2
3
|
import { type CaptionSchema } from '../caption/core.js';
|
|
3
4
|
export interface TableDataSchema extends Schema {
|
|
4
5
|
name: 'tableData';
|
|
5
6
|
type: 'inliner';
|
|
6
7
|
linkable: false;
|
|
7
|
-
Data: undefined;
|
|
8
|
+
Data: TableDataData | undefined;
|
|
8
9
|
Storage: undefined;
|
|
9
10
|
Children: InlinerSchema[];
|
|
10
11
|
}
|
|
12
|
+
export interface TableDataData {
|
|
13
|
+
center?: boolean;
|
|
14
|
+
right?: boolean;
|
|
15
|
+
freeze?: boolean;
|
|
16
|
+
}
|
|
11
17
|
export declare const tableDataSchema: TableDataSchema;
|
|
12
|
-
export declare const Td: import("../../tag.js").ToEruditTag<TableDataSchema, "Td",
|
|
18
|
+
export declare const Td: import("../../tag.js").ToEruditTag<TableDataSchema, "Td", {
|
|
19
|
+
freeze?: true;
|
|
20
|
+
} & XOR<{
|
|
21
|
+
center?: true;
|
|
22
|
+
}, {
|
|
23
|
+
right?: true;
|
|
24
|
+
}>>;
|
|
13
25
|
export interface TableRowSchema extends Schema {
|
|
14
26
|
name: 'tableRow';
|
|
15
27
|
type: 'inliner';
|
|
@@ -32,7 +44,13 @@ export declare const tableSchema: TableSchema;
|
|
|
32
44
|
export declare const Table: import("../../tag.js").ToEruditTag<TableSchema, "Table", unknown>;
|
|
33
45
|
declare const _default: [{
|
|
34
46
|
readonly schema: TableDataSchema;
|
|
35
|
-
readonly tags: [import("../../tag.js").ToEruditTag<TableDataSchema, "Td",
|
|
47
|
+
readonly tags: [import("../../tag.js").ToEruditTag<TableDataSchema, "Td", {
|
|
48
|
+
freeze?: true;
|
|
49
|
+
} & XOR<{
|
|
50
|
+
center?: true;
|
|
51
|
+
}, {
|
|
52
|
+
right?: true;
|
|
53
|
+
}>>];
|
|
36
54
|
}, {
|
|
37
55
|
readonly schema: TableRowSchema;
|
|
38
56
|
readonly tags: [import("../../tag.js").ToEruditTag<TableRowSchema, "Tr", unknown>];
|
|
@@ -10,9 +10,21 @@ export const tableDataSchema = defineSchema({
|
|
|
10
10
|
export const Td = defineEruditTag({
|
|
11
11
|
tagName: "Td",
|
|
12
12
|
schema: tableDataSchema
|
|
13
|
-
})(({ element, children, tagName }) => {
|
|
13
|
+
})(({ props, element, children, tagName }) => {
|
|
14
14
|
ensureTagInlinerChildren(tagName, children);
|
|
15
15
|
element.children = children;
|
|
16
|
+
element.data = {};
|
|
17
|
+
if (props.center) {
|
|
18
|
+
element.data = { center: true };
|
|
19
|
+
} else if (props.right) {
|
|
20
|
+
element.data = { right: true };
|
|
21
|
+
}
|
|
22
|
+
if (props.freeze) {
|
|
23
|
+
element.data.freeze = true;
|
|
24
|
+
}
|
|
25
|
+
if (Object.keys(element.data).length === 0) {
|
|
26
|
+
delete element.data;
|
|
27
|
+
}
|
|
16
28
|
});
|
|
17
29
|
export const tableRowSchema = defineSchema({
|
|
18
30
|
name: "tableRow",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
-
<path d="
|
|
2
|
+
<path d="M4.89,21.22c-.54,0-1.01-.23-1.47-.62-.39-.39-.62-.85-.62-1.47V4.89c0-.62.23-1.08.62-1.47.39-.39.85-.62,1.47-.62h14.32c.54,0,1.01.23,1.47.62.46.39.62.85.62,1.47v14.32c0,.54-.23,1.01-.62,1.47-.39.39-.85.62-1.47.62H4.89v-.08ZM11.01,15.11h-6.12v4.1h6.12s0-4.1,0-4.1ZM13.02,15.11v4.1h6.12v-4.1s-6.12,0-6.12,0ZM11.01,13.02v-4.1h-6.12v4.1h6.12ZM13.02,13.02h6.12v-4.1h-6.12s0,4.1,0,4.1ZM4.89,6.9h14.32v-2.01H4.89v2.01Z"/>
|
|
3
3
|
</svg>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
-
<path d="M10.
|
|
2
|
+
<path d="M10.67,15.66l4.83-3.08c.25-.17.33-.33.33-.58s-.08-.5-.33-.58l-4.83-3.08c-.25-.17-.5-.17-.75,0s-.42.33-.42.67v6.16c0,.33.17.5.42.67s.5.17.75,0v-.17ZM12,21.82c-1.33,0-2.66-.25-3.83-.75s-2.25-1.25-3.16-2.08-1.58-1.91-2.08-3.16-.75-2.5-.75-3.83.25-2.66.75-3.83,1.25-2.25,2.08-3.16,1.91-1.58,3.16-2.08,2.5-.75,3.83-.75,2.66.25,3.83.75c1.17.5,2.25,1.25,3.16,2.08s1.58,1.91,2.08,3.16.75,2.5.75,3.83-.25,2.66-.75,3.83c-.5,1.17-1.25,2.25-2.08,3.16s-1.91,1.58-3.16,2.08-2.5.75-3.83.75ZM12,19.91c2.16,0,4.08-.75,5.58-2.33s2.33-3.41,2.33-5.58-.75-4.08-2.33-5.58-3.41-2.33-5.58-2.33-4.08.75-5.58,2.33-2.33,3.41-2.33,5.58.75,4.08,2.33,5.58,3.41,2.33,5.58,2.33Z"/>
|
|
3
3
|
</svg>
|
package/dist/snippet.d.ts
CHANGED
|
@@ -52,6 +52,7 @@ export declare function toKeySnippet(snippet?: Snippet): KeySnippet | undefined;
|
|
|
52
52
|
export interface SeoSnippet {
|
|
53
53
|
title: string;
|
|
54
54
|
description?: string;
|
|
55
|
+
titleInherited: boolean;
|
|
55
56
|
}
|
|
56
57
|
export declare function toSeoSnippet(snippet?: Snippet): SeoSnippet | undefined;
|
|
57
58
|
export interface ResolvedSnippet {
|
package/dist/snippet.js
CHANGED
|
@@ -118,21 +118,26 @@ export function toSeoSnippet(snippet) {
|
|
|
118
118
|
if (snippet.seo === true) {
|
|
119
119
|
return {
|
|
120
120
|
title: snippet.title,
|
|
121
|
-
description: snippet.description
|
|
121
|
+
description: snippet.description,
|
|
122
|
+
titleInherited: true
|
|
122
123
|
};
|
|
123
124
|
}
|
|
124
125
|
if (typeof snippet.seo === "string") {
|
|
125
|
-
const
|
|
126
|
+
const manualTitle = snippet.seo.trim();
|
|
127
|
+
const title = manualTitle || snippet.title;
|
|
126
128
|
return {
|
|
127
129
|
title,
|
|
128
|
-
description: snippet.description
|
|
130
|
+
description: snippet.description,
|
|
131
|
+
titleInherited: !manualTitle
|
|
129
132
|
};
|
|
130
133
|
}
|
|
131
|
-
const
|
|
134
|
+
const manualTitle = snippet.seo.title?.trim();
|
|
135
|
+
const title = manualTitle || snippet.title;
|
|
132
136
|
const description = snippet.seo.description?.trim() || snippet.description;
|
|
133
137
|
return {
|
|
134
138
|
title,
|
|
135
|
-
description
|
|
139
|
+
description,
|
|
140
|
+
titleInherited: !manualTitle
|
|
136
141
|
};
|
|
137
142
|
}
|
|
138
143
|
export const snippetHook = defineRawToProseHook(({ task: context, result }) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@erudit-js/prose",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.3.0",
|
|
5
5
|
"description": "📝 JSX prose subsystem for Erudit sites",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -32,17 +32,18 @@
|
|
|
32
32
|
"prepack": "bun run build"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@erudit-js/core": "4.
|
|
36
|
-
"@floating-ui/vue": "^1.1.
|
|
37
|
-
"tsprose": "^1.0.0",
|
|
35
|
+
"@erudit-js/core": "4.3.0",
|
|
36
|
+
"@floating-ui/vue": "^1.1.11",
|
|
38
37
|
"image-size": "^2.0.2",
|
|
39
|
-
"katex": "^0.16.
|
|
38
|
+
"katex": "^0.16.33",
|
|
39
|
+
"mermaid": "^11.12.3",
|
|
40
40
|
"photoswipe": "^5.4.4",
|
|
41
|
-
"
|
|
41
|
+
"tsprose": "^1.0.1",
|
|
42
|
+
"vue": "latest"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
|
-
"glob": "13.0.
|
|
45
|
-
"oxc-transform": "0.
|
|
45
|
+
"glob": "^13.0.6",
|
|
46
|
+
"oxc-transform": "0.116.0",
|
|
46
47
|
"ts-xor": "^1.3.0"
|
|
47
48
|
}
|
|
48
49
|
}
|