@agent-native/core 0.101.10 → 0.101.11
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +23 -1
- package/corpus/core/src/a2a/handlers.ts +65 -1
- package/corpus/core/src/a2a/server.ts +124 -0
- package/corpus/core/src/a2a/task-store.ts +253 -0
- package/corpus/core/src/a2a/types.ts +26 -0
- package/corpus/core/src/agent/production-agent.ts +5 -2
- package/corpus/core/src/mcp/builtin-tools.ts +25 -1
- package/corpus/core/src/scripts/call-agent.ts +22 -3
- package/corpus/core/src/server/agent-chat-plugin.ts +86 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +23 -0
- package/corpus/templates/content/app/components/editor/MathRenderer.tsx +43 -0
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +260 -4
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +78 -0
- package/corpus/templates/content/app/global.css +53 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +13 -0
- package/corpus/templates/content/app/i18n-data.ts +163 -0
- package/corpus/templates/content/app/root.tsx +2 -0
- package/corpus/templates/content/changelog/2026-07-14-latex-equations-render-in-documents-public-pages-and-exports.md +6 -0
- package/corpus/templates/content/package.json +1 -0
- package/corpus/templates/content/shared/document-export.ts +296 -25
- package/corpus/templates/content/shared/inline-math.ts +128 -0
- package/corpus/templates/content/shared/math-rendering.ts +50 -0
- package/corpus/templates/content/shared/nfm.ts +17 -14
- package/corpus/templates/content/vite.config.ts +0 -1
- package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +17 -0
- package/corpus/templates/mail/AGENTS.md +21 -19
- package/corpus/templates/mail/actions/create-attachment-upload.ts +49 -0
- package/corpus/templates/mail/changelog/2026-07-14-connected-agents-can-now-securely-upload-local-files-for-mai.md +6 -0
- package/corpus/templates/mail/server/handlers/media.ts +84 -61
- package/corpus/templates/mail/server/lib/attachment-upload-ticket.ts +234 -0
- package/corpus/templates/mail/server/lib/mail-connector-catalog.ts +8 -4
- package/corpus/templates/mail/server/lib/media-upload.ts +114 -0
- package/corpus/templates/mail/server/plugins/auth.ts +9 -1
- package/corpus/templates/mail/server/routes/api/media/attachment-upload/[uploadId].put.ts +1 -0
- package/dist/a2a/client.d.ts +5 -1
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +18 -1
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +57 -8
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js +87 -1
- package/dist/a2a/server.js.map +1 -1
- package/dist/a2a/task-store.d.ts +27 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +216 -0
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/a2a/types.d.ts +24 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +5 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +22 -3
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/scripts/call-agent.d.ts +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +19 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +71 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
|
@@ -2729,6 +2729,8 @@ const editorSlashMessages = {
|
|
|
2729
2729
|
audio: "Audio",
|
|
2730
2730
|
audioDescription: "Upload or embed audio",
|
|
2731
2731
|
basicBlocks: "Basic blocks",
|
|
2732
|
+
blockEquation: "Block equation",
|
|
2733
|
+
blockEquationDescription: "Display a LaTeX equation on its own line",
|
|
2732
2734
|
blocks: "Blocks",
|
|
2733
2735
|
bulletedList: "Bulleted list",
|
|
2734
2736
|
bulletedListDescription: "Unordered list",
|
|
@@ -2739,6 +2741,7 @@ const editorSlashMessages = {
|
|
|
2739
2741
|
collapsibleBlockDescription: "Collapsible block",
|
|
2740
2742
|
database: "Database",
|
|
2741
2743
|
databaseDescription: "Inline database in this page",
|
|
2744
|
+
cancelEquation: "Cancel",
|
|
2742
2745
|
divider: "Divider",
|
|
2743
2746
|
dividerDescription: "Horizontal rule",
|
|
2744
2747
|
generate: "Generate",
|
|
@@ -2749,6 +2752,9 @@ const editorSlashMessages = {
|
|
|
2749
2752
|
heading4Description: "Subheading",
|
|
2750
2753
|
image: "Image",
|
|
2751
2754
|
imageDescription: "Upload or embed image",
|
|
2755
|
+
inlineEquation: "Inline equation",
|
|
2756
|
+
inlineEquationDescription: "Add a LaTeX equation within text",
|
|
2757
|
+
insertEquation: "Insert",
|
|
2752
2758
|
localComponents: "Local components",
|
|
2753
2759
|
media: "Media",
|
|
2754
2760
|
numberedList: "Numbered list",
|
|
@@ -2756,6 +2762,14 @@ const editorSlashMessages = {
|
|
|
2756
2762
|
page: "Page",
|
|
2757
2763
|
pageDescription: "Create a child page",
|
|
2758
2764
|
pages: "Pages",
|
|
2765
|
+
equationInputLabel: "LaTeX equation",
|
|
2766
|
+
equationInsertFailed: "Could not insert the equation.",
|
|
2767
|
+
equationNeedsRepair: "Fix the LaTeX to see a preview.",
|
|
2768
|
+
equationPlaceholder: String.raw`Type LaTeX, for example \frac{a}{b}`,
|
|
2769
|
+
equationPreview: "Preview",
|
|
2770
|
+
equationPreviewEmpty: "Your equation will appear here.",
|
|
2771
|
+
equationSubmitHint:
|
|
2772
|
+
"Preview updates as you type. Press Cmd/Ctrl+Enter to insert.",
|
|
2759
2773
|
quote: "Quote",
|
|
2760
2774
|
quoteDescription: "Block quote",
|
|
2761
2775
|
table: "Table",
|
|
@@ -10293,6 +10307,21 @@ const contentExactEnglishTranslations = {
|
|
|
10293
10307
|
"zh-CN": {
|
|
10294
10308
|
editor: {
|
|
10295
10309
|
failedToCreatePage: "创建页面失败",
|
|
10310
|
+
slash: {
|
|
10311
|
+
blockEquation: "块级公式",
|
|
10312
|
+
blockEquationDescription: "在单独一行显示 LaTeX 公式",
|
|
10313
|
+
cancelEquation: "取消",
|
|
10314
|
+
inlineEquation: "行内公式",
|
|
10315
|
+
inlineEquationDescription: "在文本中添加 LaTeX 公式",
|
|
10316
|
+
insertEquation: "插入",
|
|
10317
|
+
equationInputLabel: "LaTeX 公式",
|
|
10318
|
+
equationInsertFailed: "无法插入公式。",
|
|
10319
|
+
equationNeedsRepair: "请修正 LaTeX 以查看预览。",
|
|
10320
|
+
equationPlaceholder: String.raw`输入 LaTeX,例如 \frac{a}{b}`,
|
|
10321
|
+
equationPreview: "预览",
|
|
10322
|
+
equationPreviewEmpty: "公式将在此处显示。",
|
|
10323
|
+
equationSubmitHint: "输入时会实时更新预览。按 Cmd/Ctrl+Enter 插入。",
|
|
10324
|
+
},
|
|
10296
10325
|
toolbar: {
|
|
10297
10326
|
copiedPageLink: "已复制页面链接",
|
|
10298
10327
|
copyPageLink: "复制页面链接",
|
|
@@ -10306,6 +10335,23 @@ const contentExactEnglishTranslations = {
|
|
|
10306
10335
|
"es-ES": {
|
|
10307
10336
|
editor: {
|
|
10308
10337
|
failedToCreatePage: "No se pudo crear la página",
|
|
10338
|
+
slash: {
|
|
10339
|
+
blockEquation: "Ecuación en bloque",
|
|
10340
|
+
blockEquationDescription:
|
|
10341
|
+
"Muestra una ecuación LaTeX en su propia línea",
|
|
10342
|
+
cancelEquation: "Cancelar",
|
|
10343
|
+
inlineEquation: "Ecuación en línea",
|
|
10344
|
+
inlineEquationDescription: "Añade una ecuación LaTeX dentro del texto",
|
|
10345
|
+
insertEquation: "Insertar",
|
|
10346
|
+
equationInputLabel: "Ecuación LaTeX",
|
|
10347
|
+
equationInsertFailed: "No se pudo insertar la ecuación.",
|
|
10348
|
+
equationNeedsRepair: "Corrige el LaTeX para ver la vista previa.",
|
|
10349
|
+
equationPlaceholder: String.raw`Escribe LaTeX, por ejemplo \frac{a}{b}`,
|
|
10350
|
+
equationPreview: "Vista previa",
|
|
10351
|
+
equationPreviewEmpty: "Tu ecuación aparecerá aquí.",
|
|
10352
|
+
equationSubmitHint:
|
|
10353
|
+
"La vista previa se actualiza mientras escribes. Pulsa Cmd/Ctrl+Enter para insertar.",
|
|
10354
|
+
},
|
|
10309
10355
|
toolbar: {
|
|
10310
10356
|
copiedPageLink: "Enlace de página copiado",
|
|
10311
10357
|
copyPageLink: "Copiar enlace de página",
|
|
@@ -10320,6 +10366,23 @@ const contentExactEnglishTranslations = {
|
|
|
10320
10366
|
"fr-FR": {
|
|
10321
10367
|
editor: {
|
|
10322
10368
|
failedToCreatePage: "Impossible de créer la page",
|
|
10369
|
+
slash: {
|
|
10370
|
+
blockEquation: "Équation en bloc",
|
|
10371
|
+
blockEquationDescription:
|
|
10372
|
+
"Afficher une équation LaTeX sur sa propre ligne",
|
|
10373
|
+
cancelEquation: "Annuler",
|
|
10374
|
+
inlineEquation: "Équation en ligne",
|
|
10375
|
+
inlineEquationDescription: "Ajouter une équation LaTeX dans le texte",
|
|
10376
|
+
insertEquation: "Insérer",
|
|
10377
|
+
equationInputLabel: "Équation LaTeX",
|
|
10378
|
+
equationInsertFailed: "Impossible d’insérer l’équation.",
|
|
10379
|
+
equationNeedsRepair: "Corrigez le LaTeX pour afficher un aperçu.",
|
|
10380
|
+
equationPlaceholder: String.raw`Saisissez du LaTeX, par exemple \frac{a}{b}`,
|
|
10381
|
+
equationPreview: "Aperçu",
|
|
10382
|
+
equationPreviewEmpty: "Votre équation apparaîtra ici.",
|
|
10383
|
+
equationSubmitHint:
|
|
10384
|
+
"L’aperçu se met à jour pendant la saisie. Appuyez sur Cmd/Ctrl+Entrée pour insérer.",
|
|
10385
|
+
},
|
|
10323
10386
|
toolbar: {
|
|
10324
10387
|
copiedPageLink: "Lien de la page copié",
|
|
10325
10388
|
copyPageLink: "Copier le lien de la page",
|
|
@@ -10334,6 +10397,24 @@ const contentExactEnglishTranslations = {
|
|
|
10334
10397
|
"de-DE": {
|
|
10335
10398
|
editor: {
|
|
10336
10399
|
failedToCreatePage: "Seite konnte nicht erstellt werden",
|
|
10400
|
+
slash: {
|
|
10401
|
+
blockEquation: "Blockgleichung",
|
|
10402
|
+
blockEquationDescription:
|
|
10403
|
+
"Eine LaTeX-Gleichung in einer eigenen Zeile anzeigen",
|
|
10404
|
+
cancelEquation: "Abbrechen",
|
|
10405
|
+
inlineEquation: "Inline-Gleichung",
|
|
10406
|
+
inlineEquationDescription: "Eine LaTeX-Gleichung in Text einfügen",
|
|
10407
|
+
insertEquation: "Einfügen",
|
|
10408
|
+
equationInputLabel: "LaTeX-Gleichung",
|
|
10409
|
+
equationInsertFailed: "Die Gleichung konnte nicht eingefügt werden.",
|
|
10410
|
+
equationNeedsRepair:
|
|
10411
|
+
"Korrigieren Sie das LaTeX, um eine Vorschau zu sehen.",
|
|
10412
|
+
equationPlaceholder: String.raw`LaTeX eingeben, zum Beispiel \frac{a}{b}`,
|
|
10413
|
+
equationPreview: "Vorschau",
|
|
10414
|
+
equationPreviewEmpty: "Ihre Gleichung wird hier angezeigt.",
|
|
10415
|
+
equationSubmitHint:
|
|
10416
|
+
"Die Vorschau wird während der Eingabe aktualisiert. Mit Cmd/Ctrl+Enter einfügen.",
|
|
10417
|
+
},
|
|
10337
10418
|
toolbar: {
|
|
10338
10419
|
copiedPageLink: "Seitenlink kopiert",
|
|
10339
10420
|
copyPageLink: "Seitenlink kopieren",
|
|
@@ -10348,6 +10429,23 @@ const contentExactEnglishTranslations = {
|
|
|
10348
10429
|
"ja-JP": {
|
|
10349
10430
|
editor: {
|
|
10350
10431
|
failedToCreatePage: "ページを作成できませんでした",
|
|
10432
|
+
slash: {
|
|
10433
|
+
blockEquation: "ブロック数式",
|
|
10434
|
+
blockEquationDescription: "LaTeX 数式を独立した行に表示します",
|
|
10435
|
+
cancelEquation: "キャンセル",
|
|
10436
|
+
inlineEquation: "インライン数式",
|
|
10437
|
+
inlineEquationDescription: "テキスト内に LaTeX 数式を追加します",
|
|
10438
|
+
insertEquation: "挿入",
|
|
10439
|
+
equationInputLabel: "LaTeX 数式",
|
|
10440
|
+
equationInsertFailed: "数式を挿入できませんでした。",
|
|
10441
|
+
equationNeedsRepair:
|
|
10442
|
+
"プレビューを表示するには LaTeX を修正してください。",
|
|
10443
|
+
equationPlaceholder: String.raw`LaTeX を入力(例:\frac{a}{b})`,
|
|
10444
|
+
equationPreview: "プレビュー",
|
|
10445
|
+
equationPreviewEmpty: "ここに数式が表示されます。",
|
|
10446
|
+
equationSubmitHint:
|
|
10447
|
+
"入力中にプレビューが更新されます。Cmd/Ctrl+Enter で挿入します。",
|
|
10448
|
+
},
|
|
10351
10449
|
toolbar: {
|
|
10352
10450
|
copiedPageLink: "ページリンクをコピーしました",
|
|
10353
10451
|
copyPageLink: "ページリンクをコピー",
|
|
@@ -10362,6 +10460,22 @@ const contentExactEnglishTranslations = {
|
|
|
10362
10460
|
"ko-KR": {
|
|
10363
10461
|
editor: {
|
|
10364
10462
|
failedToCreatePage: "페이지를 만들지 못했습니다",
|
|
10463
|
+
slash: {
|
|
10464
|
+
blockEquation: "블록 수식",
|
|
10465
|
+
blockEquationDescription: "LaTeX 수식을 별도 줄에 표시합니다",
|
|
10466
|
+
cancelEquation: "취소",
|
|
10467
|
+
inlineEquation: "인라인 수식",
|
|
10468
|
+
inlineEquationDescription: "텍스트 안에 LaTeX 수식을 추가합니다",
|
|
10469
|
+
insertEquation: "삽입",
|
|
10470
|
+
equationInputLabel: "LaTeX 수식",
|
|
10471
|
+
equationInsertFailed: "수식을 삽입하지 못했습니다.",
|
|
10472
|
+
equationNeedsRepair: "미리 보려면 LaTeX를 수정하세요.",
|
|
10473
|
+
equationPlaceholder: String.raw`LaTeX를 입력하세요. 예: \frac{a}{b}`,
|
|
10474
|
+
equationPreview: "미리보기",
|
|
10475
|
+
equationPreviewEmpty: "여기에 수식이 표시됩니다.",
|
|
10476
|
+
equationSubmitHint:
|
|
10477
|
+
"입력하는 동안 미리보기가 업데이트됩니다. Cmd/Ctrl+Enter로 삽입하세요.",
|
|
10478
|
+
},
|
|
10365
10479
|
toolbar: {
|
|
10366
10480
|
copiedPageLink: "페이지 링크를 복사했습니다",
|
|
10367
10481
|
copyPageLink: "페이지 링크 복사",
|
|
@@ -10376,6 +10490,23 @@ const contentExactEnglishTranslations = {
|
|
|
10376
10490
|
"pt-BR": {
|
|
10377
10491
|
editor: {
|
|
10378
10492
|
failedToCreatePage: "Não foi possível criar a página",
|
|
10493
|
+
slash: {
|
|
10494
|
+
blockEquation: "Equação em bloco",
|
|
10495
|
+
blockEquationDescription:
|
|
10496
|
+
"Exiba uma equação LaTeX em uma linha própria",
|
|
10497
|
+
cancelEquation: "Cancelar",
|
|
10498
|
+
inlineEquation: "Equação em linha",
|
|
10499
|
+
inlineEquationDescription: "Adicione uma equação LaTeX dentro do texto",
|
|
10500
|
+
insertEquation: "Inserir",
|
|
10501
|
+
equationInputLabel: "Equação LaTeX",
|
|
10502
|
+
equationInsertFailed: "Não foi possível inserir a equação.",
|
|
10503
|
+
equationNeedsRepair: "Corrija o LaTeX para ver uma prévia.",
|
|
10504
|
+
equationPlaceholder: String.raw`Digite LaTeX, por exemplo \frac{a}{b}`,
|
|
10505
|
+
equationPreview: "Prévia",
|
|
10506
|
+
equationPreviewEmpty: "Sua equação aparecerá aqui.",
|
|
10507
|
+
equationSubmitHint:
|
|
10508
|
+
"A prévia é atualizada enquanto você digita. Pressione Cmd/Ctrl+Enter para inserir.",
|
|
10509
|
+
},
|
|
10379
10510
|
toolbar: {
|
|
10380
10511
|
copiedPageLink: "Link da página copiado",
|
|
10381
10512
|
copyPageLink: "Copiar link da página",
|
|
@@ -10390,6 +10521,22 @@ const contentExactEnglishTranslations = {
|
|
|
10390
10521
|
"hi-IN": {
|
|
10391
10522
|
editor: {
|
|
10392
10523
|
failedToCreatePage: "पेज नहीं बनाया जा सका",
|
|
10524
|
+
slash: {
|
|
10525
|
+
blockEquation: "ब्लॉक समीकरण",
|
|
10526
|
+
blockEquationDescription: "LaTeX समीकरण को अलग पंक्ति में दिखाएँ",
|
|
10527
|
+
cancelEquation: "रद्द करें",
|
|
10528
|
+
inlineEquation: "इनलाइन समीकरण",
|
|
10529
|
+
inlineEquationDescription: "टेक्स्ट के भीतर LaTeX समीकरण जोड़ें",
|
|
10530
|
+
insertEquation: "सम्मिलित करें",
|
|
10531
|
+
equationInputLabel: "LaTeX समीकरण",
|
|
10532
|
+
equationInsertFailed: "समीकरण सम्मिलित नहीं किया जा सका।",
|
|
10533
|
+
equationNeedsRepair: "पूर्वावलोकन देखने के लिए LaTeX ठीक करें।",
|
|
10534
|
+
equationPlaceholder: String.raw`LaTeX लिखें, जैसे \frac{a}{b}`,
|
|
10535
|
+
equationPreview: "पूर्वावलोकन",
|
|
10536
|
+
equationPreviewEmpty: "आपका समीकरण यहाँ दिखाई देगा।",
|
|
10537
|
+
equationSubmitHint:
|
|
10538
|
+
"लिखते समय पूर्वावलोकन अपडेट होता है। सम्मिलित करने के लिए Cmd/Ctrl+Enter दबाएँ।",
|
|
10539
|
+
},
|
|
10393
10540
|
toolbar: {
|
|
10394
10541
|
copiedPageLink: "पेज लिंक कॉपी किया गया",
|
|
10395
10542
|
copyPageLink: "पेज लिंक कॉपी करें",
|
|
@@ -10403,6 +10550,22 @@ const contentExactEnglishTranslations = {
|
|
|
10403
10550
|
"ar-SA": {
|
|
10404
10551
|
editor: {
|
|
10405
10552
|
failedToCreatePage: "تعذر إنشاء الصفحة",
|
|
10553
|
+
slash: {
|
|
10554
|
+
blockEquation: "معادلة مستقلة",
|
|
10555
|
+
blockEquationDescription: "اعرض معادلة LaTeX في سطر مستقل",
|
|
10556
|
+
cancelEquation: "إلغاء",
|
|
10557
|
+
inlineEquation: "معادلة ضمن السطر",
|
|
10558
|
+
inlineEquationDescription: "أضف معادلة LaTeX داخل النص",
|
|
10559
|
+
insertEquation: "إدراج",
|
|
10560
|
+
equationInputLabel: "معادلة LaTeX",
|
|
10561
|
+
equationInsertFailed: "تعذر إدراج المعادلة.",
|
|
10562
|
+
equationNeedsRepair: "صحح LaTeX لعرض المعاينة.",
|
|
10563
|
+
equationPlaceholder: String.raw`اكتب LaTeX، مثل \frac{a}{b}`,
|
|
10564
|
+
equationPreview: "معاينة",
|
|
10565
|
+
equationPreviewEmpty: "ستظهر معادلتك هنا.",
|
|
10566
|
+
equationSubmitHint:
|
|
10567
|
+
"تتحدث المعاينة أثناء الكتابة. اضغط Cmd/Ctrl+Enter للإدراج.",
|
|
10568
|
+
},
|
|
10406
10569
|
toolbar: {
|
|
10407
10570
|
copiedPageLink: "تم نسخ رابط الصفحة",
|
|
10408
10571
|
copyPageLink: "نسخ رابط الصفحة",
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
} from "./lib/content-command-search";
|
|
63
63
|
|
|
64
64
|
import stylesheet from "./global.css?url";
|
|
65
|
+
import katexStylesheet from "katex/dist/katex.min.css?url";
|
|
65
66
|
configureTracking({
|
|
66
67
|
getDefaultProps: (_name, properties) => ({
|
|
67
68
|
...properties,
|
|
@@ -71,6 +72,7 @@ configureTracking({
|
|
|
71
72
|
|
|
72
73
|
export const links: LinksFunction = () => [
|
|
73
74
|
{ rel: "stylesheet", href: stylesheet },
|
|
75
|
+
{ rel: "stylesheet", href: katexStylesheet },
|
|
74
76
|
];
|
|
75
77
|
|
|
76
78
|
interface RootLoaderData {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: added
|
|
3
|
+
date: 2026-07-14
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Create inline or block LaTeX equations from the slash menu or by typing `$…$`, with live validation and preview; existing GitHub-style equation syntax remains compatible, and equations render on public pages and in exports.
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { matchInlineMathAt } from "./inline-math.js";
|
|
2
|
+
import { KATEX_STYLESHEET_URL, renderMathToHtml } from "./math-rendering.js";
|
|
3
|
+
|
|
1
4
|
export type DocumentExportFormat = "pdf" | "markdown" | "html";
|
|
2
5
|
|
|
3
6
|
export interface DocumentExportInput {
|
|
@@ -100,18 +103,37 @@ export function markdownWithTitle(
|
|
|
100
103
|
return `${`# ${safeTitle}`}${body ? `\n\n${body}` : ""}\n`;
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
|
|
106
|
+
interface InlineExportToken {
|
|
107
|
+
marker: string;
|
|
108
|
+
html: string;
|
|
109
|
+
source: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function restoreTokenSources(
|
|
113
|
+
text: string,
|
|
114
|
+
tokens: InlineExportToken[],
|
|
115
|
+
): string {
|
|
116
|
+
return tokens.reduce(
|
|
117
|
+
(restored, token) =>
|
|
118
|
+
restored.split(token.marker).join(escapeHtml(token.source)),
|
|
119
|
+
text,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function inlineMarkdownSegmentToHtml(
|
|
124
|
+
text: string,
|
|
125
|
+
tokens: InlineExportToken[],
|
|
126
|
+
): string {
|
|
104
127
|
return escapeHtml(text)
|
|
105
128
|
.replace(
|
|
106
129
|
/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^&]*")?\)/g,
|
|
107
130
|
(_m, alt, src) => {
|
|
108
|
-
return `<img src="${safeExportUrl(src, "image")}" alt="${alt}" />`;
|
|
131
|
+
return `<img src="${safeExportUrl(src, "image")}" alt="${restoreTokenSources(alt, tokens)}" />`;
|
|
109
132
|
},
|
|
110
133
|
)
|
|
111
134
|
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_m, label, href) => {
|
|
112
135
|
return `<a href="${safeExportUrl(href, "link")}">${label}</a>`;
|
|
113
136
|
})
|
|
114
|
-
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
|
115
137
|
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
|
116
138
|
.replace(/__([^_]+)__/g, "<strong>$1</strong>")
|
|
117
139
|
.replace(/~~([^~]+)~~/g, "<s>$1</s>")
|
|
@@ -119,24 +141,229 @@ function inlineMarkdownToHtml(text: string): string {
|
|
|
119
141
|
.replace(/(^|[^_])_([^_]+)_/g, "$1<em>$2</em>");
|
|
120
142
|
}
|
|
121
143
|
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
144
|
+
function isEscapedDelimiter(text: string, index: number): boolean {
|
|
145
|
+
let backslashes = 0;
|
|
146
|
+
for (let cursor = index - 1; cursor >= 0 && text[cursor] === "\\"; cursor--) {
|
|
147
|
+
backslashes++;
|
|
148
|
+
}
|
|
149
|
+
return backslashes % 2 === 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function mathErrorHtml(
|
|
153
|
+
source: string,
|
|
154
|
+
error: string,
|
|
155
|
+
displayMode: boolean,
|
|
156
|
+
): string {
|
|
157
|
+
const className = displayMode
|
|
158
|
+
? "math-error math-error-block"
|
|
159
|
+
: "math-error math-error-inline";
|
|
160
|
+
const tagName = displayMode ? "pre" : "code";
|
|
161
|
+
return `<${tagName} class="${className}" title="${escapeHtml(error)}">${escapeHtml(source)}</${tagName}>`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function inlineMarkdownToHtml(text: string): string {
|
|
165
|
+
const tokens: InlineExportToken[] = [];
|
|
166
|
+
let markerStart = "\uE000agent-native-inline-";
|
|
167
|
+
while (text.includes(markerStart)) markerStart += "-";
|
|
168
|
+
|
|
169
|
+
const protectedText: string[] = [];
|
|
170
|
+
let cursor = 0;
|
|
171
|
+
|
|
172
|
+
while (cursor < text.length) {
|
|
173
|
+
if (text.startsWith("$`", cursor) && isEscapedDelimiter(text, cursor)) {
|
|
174
|
+
const close = text.indexOf("`$", cursor + 2);
|
|
175
|
+
if (close !== -1) {
|
|
176
|
+
protectedText.push(text.slice(cursor, close + 2));
|
|
177
|
+
cursor = close + 2;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (text[cursor] === "$") {
|
|
183
|
+
const inlineMath = matchInlineMathAt(text, cursor);
|
|
184
|
+
if (inlineMath) {
|
|
185
|
+
const { latex } = inlineMath;
|
|
186
|
+
const source = text.slice(cursor, inlineMath.to);
|
|
187
|
+
const math = renderMathToHtml(latex, false);
|
|
188
|
+
const marker = `${markerStart}${tokens.length}\uE001`;
|
|
189
|
+
tokens.push({
|
|
190
|
+
marker,
|
|
191
|
+
source,
|
|
192
|
+
html: math.ok
|
|
193
|
+
? `<span class="math-inline">${math.html}</span>`
|
|
194
|
+
: mathErrorHtml(source, math.error, false),
|
|
195
|
+
});
|
|
196
|
+
protectedText.push(marker);
|
|
197
|
+
cursor = inlineMath.to;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (text[cursor] === "`" && !isEscapedDelimiter(text, cursor)) {
|
|
203
|
+
let delimiterLength = 1;
|
|
204
|
+
while (text[cursor + delimiterLength] === "`") delimiterLength++;
|
|
205
|
+
const delimiter = "`".repeat(delimiterLength);
|
|
206
|
+
let close = text.indexOf(delimiter, cursor + delimiterLength);
|
|
207
|
+
while (
|
|
208
|
+
close !== -1 &&
|
|
209
|
+
(text[close - 1] === "`" || text[close + delimiterLength] === "`")
|
|
210
|
+
) {
|
|
211
|
+
close = text.indexOf(delimiter, close + delimiterLength);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (close !== -1) {
|
|
215
|
+
const source = text.slice(cursor, close + delimiterLength);
|
|
216
|
+
let code = text
|
|
217
|
+
.slice(cursor + delimiterLength, close)
|
|
218
|
+
.replace(/\r?\n/g, " ");
|
|
219
|
+
if (
|
|
220
|
+
code.startsWith(" ") &&
|
|
221
|
+
code.endsWith(" ") &&
|
|
222
|
+
code.trim().length > 0
|
|
223
|
+
) {
|
|
224
|
+
code = code.slice(1, -1);
|
|
225
|
+
}
|
|
226
|
+
const marker = `${markerStart}${tokens.length}\uE001`;
|
|
227
|
+
tokens.push({
|
|
228
|
+
marker,
|
|
229
|
+
source,
|
|
230
|
+
html: `<code>${escapeHtml(code)}</code>`,
|
|
231
|
+
});
|
|
232
|
+
protectedText.push(marker);
|
|
233
|
+
cursor = close + delimiterLength;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
protectedText.push(text[cursor]);
|
|
239
|
+
cursor++;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return tokens.reduce(
|
|
243
|
+
(html, token) => html.split(token.marker).join(token.html),
|
|
244
|
+
inlineMarkdownSegmentToHtml(protectedText.join(""), tokens),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function listItemsToHtml(items: string[][], ordered: boolean): string {
|
|
249
|
+
const renderedItems = items
|
|
250
|
+
.map((lines) => {
|
|
251
|
+
const task = lines[0].match(/^\[( |x|X)\]\s+(.*)$/);
|
|
129
252
|
if (task) {
|
|
130
253
|
const checked = task[1].toLowerCase() === "x";
|
|
131
254
|
return `<li class="task"><input type="checkbox" disabled${
|
|
132
255
|
checked ? " checked" : ""
|
|
133
|
-
} /> <span>${inlineMarkdownToHtml(task[2])}</span
|
|
256
|
+
} /> <span>${inlineMarkdownToHtml(task[2])}</span>${
|
|
257
|
+
lines.length > 1 ? markdownToHtml(lines.slice(1).join("\n")) : ""
|
|
258
|
+
}</li>`;
|
|
134
259
|
}
|
|
135
|
-
return
|
|
260
|
+
return lines.length === 1
|
|
261
|
+
? `<li>${inlineMarkdownToHtml(lines[0])}</li>`
|
|
262
|
+
: `<li>${markdownToHtml(lines.join("\n"))}</li>`;
|
|
136
263
|
})
|
|
137
264
|
.join("\n");
|
|
138
265
|
|
|
139
|
-
return ordered
|
|
266
|
+
return ordered
|
|
267
|
+
? `<ol>\n${renderedItems}\n</ol>`
|
|
268
|
+
: `<ul>\n${renderedItems}\n</ul>`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
interface ListMarkerMatch {
|
|
272
|
+
baseIndent: number;
|
|
273
|
+
contentIndent: number;
|
|
274
|
+
content: string;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function indentationWidth(value: string): number {
|
|
278
|
+
let width = 0;
|
|
279
|
+
for (const character of value) {
|
|
280
|
+
width += character === "\t" ? 4 - (width % 4) : 1;
|
|
281
|
+
}
|
|
282
|
+
return width;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function matchListMarker(
|
|
286
|
+
line: string,
|
|
287
|
+
ordered: boolean,
|
|
288
|
+
): ListMarkerMatch | null {
|
|
289
|
+
const match = line.match(
|
|
290
|
+
ordered
|
|
291
|
+
? /^([ \t]*)(?:\d+[.)])([ \t]+)(.*)$/
|
|
292
|
+
: /^([ \t]*)(?:[-*+])([ \t]+)(.*)$/,
|
|
293
|
+
);
|
|
294
|
+
if (!match) return null;
|
|
295
|
+
const contentStart = match[0].length - match[3].length;
|
|
296
|
+
return {
|
|
297
|
+
baseIndent: indentationWidth(match[1]),
|
|
298
|
+
contentIndent: indentationWidth(line.slice(0, contentStart)),
|
|
299
|
+
content: match[3],
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function leadingIndentWidth(line: string): number {
|
|
304
|
+
return indentationWidth(line.match(/^[ \t]*/)?.[0] ?? "");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function stripLeadingIndent(line: string, targetWidth: number): string {
|
|
308
|
+
let width = 0;
|
|
309
|
+
let index = 0;
|
|
310
|
+
while (index < line.length && width < targetWidth) {
|
|
311
|
+
const character = line[index];
|
|
312
|
+
if (character !== " " && character !== "\t") break;
|
|
313
|
+
width += character === "\t" ? 4 - (width % 4) : 1;
|
|
314
|
+
index++;
|
|
315
|
+
}
|
|
316
|
+
return line.slice(index);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function collectListItems(
|
|
320
|
+
lines: string[],
|
|
321
|
+
start: number,
|
|
322
|
+
ordered: boolean,
|
|
323
|
+
): { items: string[][]; nextIndex: number } {
|
|
324
|
+
const firstMarker = matchListMarker(lines[start], ordered);
|
|
325
|
+
if (!firstMarker) return { items: [], nextIndex: start };
|
|
326
|
+
|
|
327
|
+
const baseIndent = firstMarker.baseIndent;
|
|
328
|
+
const items: string[][] = [];
|
|
329
|
+
let index = start;
|
|
330
|
+
|
|
331
|
+
while (index < lines.length) {
|
|
332
|
+
const itemMarker = matchListMarker(lines[index], ordered);
|
|
333
|
+
if (!itemMarker || itemMarker.baseIndent !== baseIndent) break;
|
|
334
|
+
|
|
335
|
+
const item = [itemMarker.content];
|
|
336
|
+
index++;
|
|
337
|
+
|
|
338
|
+
while (index < lines.length) {
|
|
339
|
+
const nextMarker = matchListMarker(lines[index], ordered);
|
|
340
|
+
if (nextMarker?.baseIndent === baseIndent) break;
|
|
341
|
+
if (leadingIndentWidth(lines[index]) >= itemMarker.contentIndent) {
|
|
342
|
+
item.push(stripLeadingIndent(lines[index], itemMarker.contentIndent));
|
|
343
|
+
index++;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (!lines[index].trim()) {
|
|
347
|
+
let nextContent = index + 1;
|
|
348
|
+
while (nextContent < lines.length && !lines[nextContent].trim()) {
|
|
349
|
+
nextContent++;
|
|
350
|
+
}
|
|
351
|
+
if (
|
|
352
|
+
nextContent < lines.length &&
|
|
353
|
+
leadingIndentWidth(lines[nextContent]) >= itemMarker.contentIndent
|
|
354
|
+
) {
|
|
355
|
+
item.push("");
|
|
356
|
+
index++;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
items.push(item);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return { items, nextIndex: index };
|
|
140
367
|
}
|
|
141
368
|
|
|
142
369
|
function isEmptyBlockLine(trimmed: string): boolean {
|
|
@@ -163,6 +390,42 @@ function markdownToHtml(markdown: string): string {
|
|
|
163
390
|
continue;
|
|
164
391
|
}
|
|
165
392
|
|
|
393
|
+
if (trimmed === "$$") {
|
|
394
|
+
const source = [line];
|
|
395
|
+
const latex: string[] = [];
|
|
396
|
+
index++;
|
|
397
|
+
while (index < lines.length && lines[index].trim() !== "$$") {
|
|
398
|
+
source.push(lines[index]);
|
|
399
|
+
latex.push(lines[index]);
|
|
400
|
+
index++;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const closed = index < lines.length;
|
|
404
|
+
if (closed) {
|
|
405
|
+
source.push(lines[index]);
|
|
406
|
+
index++;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (!closed) {
|
|
410
|
+
blocks.push(
|
|
411
|
+
mathErrorHtml(
|
|
412
|
+
source.join("\n"),
|
|
413
|
+
"This block equation is missing its closing delimiter.",
|
|
414
|
+
true,
|
|
415
|
+
),
|
|
416
|
+
);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const math = renderMathToHtml(latex.join("\n"), true);
|
|
421
|
+
blocks.push(
|
|
422
|
+
math.ok
|
|
423
|
+
? `<div class="math-block">${math.html}</div>`
|
|
424
|
+
: mathErrorHtml(source.join("\n"), math.error, true),
|
|
425
|
+
);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
|
|
166
429
|
const codeFence = trimmed.match(/^```(\w+)?/);
|
|
167
430
|
if (codeFence) {
|
|
168
431
|
const code: string[] = [];
|
|
@@ -208,22 +471,16 @@ function markdownToHtml(markdown: string): string {
|
|
|
208
471
|
}
|
|
209
472
|
|
|
210
473
|
if (/^\s*[-*+]\s+/.test(line)) {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
index++;
|
|
215
|
-
}
|
|
216
|
-
blocks.push(listItemsToHtml(items, false));
|
|
474
|
+
const list = collectListItems(lines, index, false);
|
|
475
|
+
index = list.nextIndex;
|
|
476
|
+
blocks.push(listItemsToHtml(list.items, false));
|
|
217
477
|
continue;
|
|
218
478
|
}
|
|
219
479
|
|
|
220
480
|
if (/^\s*\d+[.)]\s+/.test(line)) {
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
index++;
|
|
225
|
-
}
|
|
226
|
-
blocks.push(listItemsToHtml(items, true));
|
|
481
|
+
const list = collectListItems(lines, index, true);
|
|
482
|
+
index = list.nextIndex;
|
|
483
|
+
blocks.push(listItemsToHtml(list.items, true));
|
|
227
484
|
continue;
|
|
228
485
|
}
|
|
229
486
|
|
|
@@ -233,6 +490,7 @@ function markdownToHtml(markdown: string): string {
|
|
|
233
490
|
index < lines.length &&
|
|
234
491
|
lines[index].trim() &&
|
|
235
492
|
!isEmptyBlockLine(lines[index].trim()) &&
|
|
493
|
+
lines[index].trim() !== "$$" &&
|
|
236
494
|
!/^(#{1,6})\s+/.test(lines[index].trim()) &&
|
|
237
495
|
!/^```/.test(lines[index].trim()) &&
|
|
238
496
|
!/^>\s?/.test(lines[index].trim()) &&
|
|
@@ -273,6 +531,7 @@ function buildHtmlDocument(input: {
|
|
|
273
531
|
<meta charset="utf-8" />
|
|
274
532
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
275
533
|
<title>${escapeHtml(input.title)}</title>
|
|
534
|
+
<link rel="stylesheet" href="${KATEX_STYLESHEET_URL}" crossorigin="anonymous" />
|
|
276
535
|
<style>
|
|
277
536
|
:root { color-scheme: light; }
|
|
278
537
|
body {
|
|
@@ -329,6 +588,18 @@ function buildHtmlDocument(input: {
|
|
|
329
588
|
border-radius: 4px;
|
|
330
589
|
padding: 0.12rem 0.3rem;
|
|
331
590
|
}
|
|
591
|
+
.math-inline { display: inline-block; max-width: 100%; vertical-align: -0.08em; }
|
|
592
|
+
.math-block { max-width: 100%; margin: 18px 0; overflow-x: auto; padding: 4px 0; }
|
|
593
|
+
.math-error {
|
|
594
|
+
border: 1px solid #fecaca;
|
|
595
|
+
border-radius: 6px;
|
|
596
|
+
background: #fef2f2;
|
|
597
|
+
color: #262626;
|
|
598
|
+
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
599
|
+
white-space: pre-wrap;
|
|
600
|
+
}
|
|
601
|
+
.math-error-inline { padding: 0.05rem 0.25rem; font-size: 0.9em; }
|
|
602
|
+
.math-error-block { overflow-wrap: anywhere; padding: 12px 14px; }
|
|
332
603
|
a { color: #2563eb; }
|
|
333
604
|
img {
|
|
334
605
|
display: block;
|