chore: update TypeScript build info cache
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+92
-86
@@ -1,137 +1,152 @@
|
||||
{
|
||||
"hash": "550e7d4b",
|
||||
"configHash": "625eb836",
|
||||
"lockfileHash": "77f35274",
|
||||
"browserHash": "a276ee59",
|
||||
"hash": "7e911bc9",
|
||||
"configHash": "6d2fe504",
|
||||
"lockfileHash": "610c0484",
|
||||
"browserHash": "449bae22",
|
||||
"optimized": {
|
||||
"@angular/cdk/a11y": {
|
||||
"src": "../../../../../../node_modules/@angular/cdk/fesm2022/a11y.mjs",
|
||||
"file": "@angular_cdk_a11y.js",
|
||||
"fileHash": "ba521394",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/common": {
|
||||
"src": "../../../../../../node_modules/@angular/common/fesm2022/common.mjs",
|
||||
"file": "@angular_common.js",
|
||||
"fileHash": "05936fd0",
|
||||
"fileHash": "7db29729",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/common/http": {
|
||||
"src": "../../../../../../node_modules/@angular/common/fesm2022/http.mjs",
|
||||
"file": "@angular_common_http.js",
|
||||
"fileHash": "7c938c00",
|
||||
"fileHash": "9fd91a95",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/common/locales/fr": {
|
||||
"src": "../../../../../../node_modules/@angular/common/locales/fr.js",
|
||||
"file": "@angular_common_locales_fr.js",
|
||||
"fileHash": "3f6dc116",
|
||||
"fileHash": "b1d8412a",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/core": {
|
||||
"src": "../../../../../../node_modules/@angular/core/fesm2022/core.mjs",
|
||||
"file": "@angular_core.js",
|
||||
"fileHash": "51703fe3",
|
||||
"fileHash": "4aab6957",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/core/rxjs-interop": {
|
||||
"src": "../../../../../../node_modules/@angular/core/fesm2022/rxjs-interop.mjs",
|
||||
"file": "@angular_core_rxjs-interop.js",
|
||||
"fileHash": "d9952fa8",
|
||||
"fileHash": "83149d22",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/forms": {
|
||||
"src": "../../../../../../node_modules/@angular/forms/fesm2022/forms.mjs",
|
||||
"file": "@angular_forms.js",
|
||||
"fileHash": "2b783004",
|
||||
"fileHash": "1fffec30",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"src": "../../../../../../node_modules/@angular/platform-browser/fesm2022/platform-browser.mjs",
|
||||
"file": "@angular_platform-browser.js",
|
||||
"fileHash": "330b2f60",
|
||||
"fileHash": "8a1d0c5e",
|
||||
"needsInterop": false
|
||||
},
|
||||
"angular-calendar": {
|
||||
"src": "../../../../../../node_modules/angular-calendar/fesm2022/angular-calendar.mjs",
|
||||
"file": "angular-calendar.js",
|
||||
"fileHash": "3f18ca62",
|
||||
"fileHash": "6ade82aa",
|
||||
"needsInterop": false
|
||||
},
|
||||
"angular-calendar/date-adapters/date-fns": {
|
||||
"src": "../../../../../../node_modules/angular-calendar/date-adapters/esm/date-fns/index.js",
|
||||
"file": "angular-calendar_date-adapters_date-fns.js",
|
||||
"fileHash": "50981191",
|
||||
"fileHash": "bc3d3c33",
|
||||
"needsInterop": false
|
||||
},
|
||||
"highlight.js": {
|
||||
"src": "../../../../../../node_modules/highlight.js/es/index.js",
|
||||
"file": "highlight__js.js",
|
||||
"fileHash": "093a5a02",
|
||||
"fileHash": "1c76d52d",
|
||||
"needsInterop": false
|
||||
},
|
||||
"markdown-it": {
|
||||
"src": "../../../../../../node_modules/markdown-it/index.mjs",
|
||||
"file": "markdown-it.js",
|
||||
"fileHash": "902d4090",
|
||||
"fileHash": "8e6c68f6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"markdown-it-anchor": {
|
||||
"src": "../../../../../../node_modules/markdown-it-anchor/dist/markdownItAnchor.mjs",
|
||||
"file": "markdown-it-anchor.js",
|
||||
"fileHash": "ba1f7b46",
|
||||
"fileHash": "5e692d12",
|
||||
"needsInterop": false
|
||||
},
|
||||
"markdown-it-attrs": {
|
||||
"src": "../../../../../../node_modules/markdown-it-attrs/index.js",
|
||||
"file": "markdown-it-attrs.js",
|
||||
"fileHash": "09d13132",
|
||||
"fileHash": "2c29aea3",
|
||||
"needsInterop": true
|
||||
},
|
||||
"markdown-it-footnote": {
|
||||
"src": "../../../../../../node_modules/markdown-it-footnote/index.js",
|
||||
"file": "markdown-it-footnote.js",
|
||||
"fileHash": "7d35f2e8",
|
||||
"fileHash": "4ea0b149",
|
||||
"needsInterop": true
|
||||
},
|
||||
"markdown-it-multimd-table": {
|
||||
"src": "../../../../../../node_modules/markdown-it-multimd-table/index.js",
|
||||
"file": "markdown-it-multimd-table.js",
|
||||
"fileHash": "26b36290",
|
||||
"fileHash": "13033d9b",
|
||||
"needsInterop": true
|
||||
},
|
||||
"markdown-it-task-lists": {
|
||||
"src": "../../../../../../node_modules/markdown-it-task-lists/index.js",
|
||||
"file": "markdown-it-task-lists.js",
|
||||
"fileHash": "283c7022",
|
||||
"fileHash": "b724aca3",
|
||||
"needsInterop": true
|
||||
},
|
||||
"mermaid": {
|
||||
"src": "../../../../../../node_modules/mermaid/dist/mermaid.core.mjs",
|
||||
"file": "mermaid.js",
|
||||
"fileHash": "6efe0ec4",
|
||||
"fileHash": "b2047cf6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"rxjs": {
|
||||
"src": "../../../../../../node_modules/rxjs/dist/esm5/index.js",
|
||||
"file": "rxjs.js",
|
||||
"fileHash": "ff4ac288",
|
||||
"fileHash": "bff53593",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"diagram-S2PKOQOG-RM7ASWFZ": {
|
||||
"file": "diagram-S2PKOQOG-RM7ASWFZ.js"
|
||||
"sankeyDiagram-TZEHDZUN-GH26R5YW": {
|
||||
"file": "sankeyDiagram-TZEHDZUN-GH26R5YW.js"
|
||||
},
|
||||
"diagram-QEK2KX5R-QLL2LDZJ": {
|
||||
"file": "diagram-QEK2KX5R-QLL2LDZJ.js"
|
||||
"diagram-S2PKOQOG-D6F7ANDO": {
|
||||
"file": "diagram-S2PKOQOG-D6F7ANDO.js"
|
||||
},
|
||||
"blockDiagram-VD42YOAC-6W666JF2": {
|
||||
"file": "blockDiagram-VD42YOAC-6W666JF2.js"
|
||||
"diagram-QEK2KX5R-KMPAAIQN": {
|
||||
"file": "diagram-QEK2KX5R-KMPAAIQN.js"
|
||||
},
|
||||
"architectureDiagram-VXUJARFQ-7JNJRGGM": {
|
||||
"file": "architectureDiagram-VXUJARFQ-7JNJRGGM.js"
|
||||
"blockDiagram-VD42YOAC-KZTUA5HK": {
|
||||
"file": "blockDiagram-VD42YOAC-KZTUA5HK.js"
|
||||
},
|
||||
"diagram-PSM6KHXK-3GNQQWOU": {
|
||||
"file": "diagram-PSM6KHXK-3GNQQWOU.js"
|
||||
"architectureDiagram-VXUJARFQ-WEBFVMSU": {
|
||||
"file": "architectureDiagram-VXUJARFQ-WEBFVMSU.js"
|
||||
},
|
||||
"diagram-PSM6KHXK-OYEWFDFJ": {
|
||||
"file": "diagram-PSM6KHXK-OYEWFDFJ.js"
|
||||
},
|
||||
"classDiagram-2ON5EDUG-NO6W7S54": {
|
||||
"file": "classDiagram-2ON5EDUG-NO6W7S54.js"
|
||||
},
|
||||
"classDiagram-v2-WZHVMYZB-J2EUDOJH": {
|
||||
"file": "classDiagram-v2-WZHVMYZB-J2EUDOJH.js"
|
||||
},
|
||||
"chunk-SOIGDKSE": {
|
||||
"file": "chunk-SOIGDKSE.js"
|
||||
},
|
||||
"stateDiagram-FKZM4ZOC-JBDO72I4": {
|
||||
"file": "stateDiagram-FKZM4ZOC-JBDO72I4.js"
|
||||
},
|
||||
@@ -150,20 +165,26 @@
|
||||
"mindmap-definition-VGOIOE7T-LIQX7OEO": {
|
||||
"file": "mindmap-definition-VGOIOE7T-LIQX7OEO.js"
|
||||
},
|
||||
"kanban-definition-3W4ZIXB7-GUMHX2OD": {
|
||||
"file": "kanban-definition-3W4ZIXB7-GUMHX2OD.js"
|
||||
"kanban-definition-3W4ZIXB7-H4KIKSB7": {
|
||||
"file": "kanban-definition-3W4ZIXB7-H4KIKSB7.js"
|
||||
},
|
||||
"sankeyDiagram-TZEHDZUN-GH26R5YW": {
|
||||
"file": "sankeyDiagram-TZEHDZUN-GH26R5YW.js"
|
||||
"gitGraphDiagram-NY62KEGX-DAVBKLGM": {
|
||||
"file": "gitGraphDiagram-NY62KEGX-DAVBKLGM.js"
|
||||
},
|
||||
"ganttDiagram-LVOFAZNH-HYMY4RKD": {
|
||||
"file": "ganttDiagram-LVOFAZNH-HYMY4RKD.js"
|
||||
},
|
||||
"infoDiagram-F6ZHWCRC-HMHRPPWW": {
|
||||
"file": "infoDiagram-F6ZHWCRC-HMHRPPWW.js"
|
||||
"infoDiagram-F6ZHWCRC-L22DC2WZ": {
|
||||
"file": "infoDiagram-F6ZHWCRC-L22DC2WZ.js"
|
||||
},
|
||||
"pieDiagram-ADFJNKIX-HTPFO6AD": {
|
||||
"file": "pieDiagram-ADFJNKIX-HTPFO6AD.js"
|
||||
"pieDiagram-ADFJNKIX-QYG3H4AZ": {
|
||||
"file": "pieDiagram-ADFJNKIX-QYG3H4AZ.js"
|
||||
},
|
||||
"chunk-PNW5KFH4": {
|
||||
"file": "chunk-PNW5KFH4.js"
|
||||
},
|
||||
"chunk-ORIZ2BG5": {
|
||||
"file": "chunk-ORIZ2BG5.js"
|
||||
},
|
||||
"quadrantDiagram-AYHSOK5B-G2SG5IZD": {
|
||||
"file": "quadrantDiagram-AYHSOK5B-G2SG5IZD.js"
|
||||
@@ -177,69 +198,63 @@
|
||||
"sequenceDiagram-WL72ISMW-O3J6HVSP": {
|
||||
"file": "sequenceDiagram-WL72ISMW-O3J6HVSP.js"
|
||||
},
|
||||
"classDiagram-2ON5EDUG-NO6W7S54": {
|
||||
"file": "classDiagram-2ON5EDUG-NO6W7S54.js"
|
||||
"chunk-3WIYXQMB": {
|
||||
"file": "chunk-3WIYXQMB.js"
|
||||
},
|
||||
"chunk-SOIGDKSE": {
|
||||
"file": "chunk-SOIGDKSE.js"
|
||||
"erDiagram-Q2GNP2WA-JTEYVNF6": {
|
||||
"file": "erDiagram-Q2GNP2WA-JTEYVNF6.js"
|
||||
},
|
||||
"info-63CPKGFF-W56KXM6Z": {
|
||||
"file": "info-63CPKGFF-W56KXM6Z.js"
|
||||
},
|
||||
"packet-HUATNLJX-LCJ3BRNR": {
|
||||
"file": "packet-HUATNLJX-LCJ3BRNR.js"
|
||||
},
|
||||
"pie-WTHONI2E-7JKUTNCJ": {
|
||||
"file": "pie-WTHONI2E-7JKUTNCJ.js"
|
||||
},
|
||||
"architecture-O4VJ6CD3-IBEWAQYB": {
|
||||
"file": "architecture-O4VJ6CD3-IBEWAQYB.js"
|
||||
},
|
||||
"gitGraph-ZV4HHKMB-6SC2CHQE": {
|
||||
"file": "gitGraph-ZV4HHKMB-6SC2CHQE.js"
|
||||
},
|
||||
"radar-NJJJXTRR-IXC2PP4O": {
|
||||
"file": "radar-NJJJXTRR-IXC2PP4O.js"
|
||||
},
|
||||
"treemap-75Q7IDZK-IP775KCD": {
|
||||
"file": "treemap-75Q7IDZK-IP775KCD.js"
|
||||
},
|
||||
"gitGraphDiagram-NY62KEGX-DAVBKLGM": {
|
||||
"file": "gitGraphDiagram-NY62KEGX-DAVBKLGM.js"
|
||||
},
|
||||
"chunk-PNW5KFH4": {
|
||||
"file": "chunk-PNW5KFH4.js"
|
||||
},
|
||||
"chunk-ORIZ2BG5": {
|
||||
"file": "chunk-ORIZ2BG5.js"
|
||||
},
|
||||
"chunk-3WIYXQMB": {
|
||||
"file": "chunk-3WIYXQMB.js"
|
||||
},
|
||||
"chunk-BUI4I457": {
|
||||
"file": "chunk-BUI4I457.js"
|
||||
},
|
||||
"packet-HUATNLJX-LCJ3BRNR": {
|
||||
"file": "packet-HUATNLJX-LCJ3BRNR.js"
|
||||
},
|
||||
"chunk-CHJ5BV6S": {
|
||||
"file": "chunk-CHJ5BV6S.js"
|
||||
},
|
||||
"pie-WTHONI2E-7JKUTNCJ": {
|
||||
"file": "pie-WTHONI2E-7JKUTNCJ.js"
|
||||
},
|
||||
"chunk-XP22GJHQ": {
|
||||
"file": "chunk-XP22GJHQ.js"
|
||||
},
|
||||
"architecture-O4VJ6CD3-IBEWAQYB": {
|
||||
"file": "architecture-O4VJ6CD3-IBEWAQYB.js"
|
||||
},
|
||||
"chunk-NYZY7JGI": {
|
||||
"file": "chunk-NYZY7JGI.js"
|
||||
},
|
||||
"gitGraph-ZV4HHKMB-6SC2CHQE": {
|
||||
"file": "gitGraph-ZV4HHKMB-6SC2CHQE.js"
|
||||
},
|
||||
"chunk-FNEVJCCX": {
|
||||
"file": "chunk-FNEVJCCX.js"
|
||||
},
|
||||
"radar-NJJJXTRR-IXC2PP4O": {
|
||||
"file": "radar-NJJJXTRR-IXC2PP4O.js"
|
||||
},
|
||||
"chunk-R33GOAXK": {
|
||||
"file": "chunk-R33GOAXK.js"
|
||||
},
|
||||
"treemap-75Q7IDZK-IP775KCD": {
|
||||
"file": "treemap-75Q7IDZK-IP775KCD.js"
|
||||
},
|
||||
"chunk-5SXTVVUG": {
|
||||
"file": "chunk-5SXTVVUG.js"
|
||||
},
|
||||
"chunk-WHHJWK6B": {
|
||||
"file": "chunk-WHHJWK6B.js"
|
||||
},
|
||||
"chunk-BSULYXPT": {
|
||||
"file": "chunk-BSULYXPT.js"
|
||||
},
|
||||
"chunk-B5NQPFQG": {
|
||||
"file": "chunk-B5NQPFQG.js"
|
||||
},
|
||||
"katex-JJTYNRHT": {
|
||||
"file": "katex-JJTYNRHT.js"
|
||||
},
|
||||
@@ -255,6 +270,9 @@
|
||||
"chunk-6SIVX7OU": {
|
||||
"file": "chunk-6SIVX7OU.js"
|
||||
},
|
||||
"chunk-NGEE2U2J": {
|
||||
"file": "chunk-NGEE2U2J.js"
|
||||
},
|
||||
"cose-bilkent-S5V4N54A-5WYXQMNH": {
|
||||
"file": "cose-bilkent-S5V4N54A-5WYXQMNH.js"
|
||||
},
|
||||
@@ -273,24 +291,12 @@
|
||||
"chunk-I4QIIVJ7": {
|
||||
"file": "chunk-I4QIIVJ7.js"
|
||||
},
|
||||
"erDiagram-Q2GNP2WA-JTEYVNF6": {
|
||||
"file": "erDiagram-Q2GNP2WA-JTEYVNF6.js"
|
||||
},
|
||||
"chunk-PLWNSIKB": {
|
||||
"file": "chunk-PLWNSIKB.js"
|
||||
},
|
||||
"chunk-LHH5RO5K": {
|
||||
"file": "chunk-LHH5RO5K.js"
|
||||
},
|
||||
"chunk-BSULYXPT": {
|
||||
"file": "chunk-BSULYXPT.js"
|
||||
},
|
||||
"chunk-B5NQPFQG": {
|
||||
"file": "chunk-B5NQPFQG.js"
|
||||
},
|
||||
"chunk-NGEE2U2J": {
|
||||
"file": "chunk-NGEE2U2J.js"
|
||||
},
|
||||
"chunk-JSZQKJT3": {
|
||||
"file": "chunk-JSZQKJT3.js"
|
||||
},
|
||||
|
||||
+5
-5
@@ -12,14 +12,14 @@ import "./chunk-FNEVJCCX.js";
|
||||
import "./chunk-R33GOAXK.js";
|
||||
import "./chunk-5SXTVVUG.js";
|
||||
import "./chunk-WHHJWK6B.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import {
|
||||
cytoscape as cytoscape2
|
||||
} from "./chunk-4434HPF7.js";
|
||||
import {
|
||||
selectSvgElement
|
||||
} from "./chunk-B5NQPFQG.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
cytoscape as cytoscape2
|
||||
} from "./chunk-4434HPF7.js";
|
||||
import {
|
||||
createText,
|
||||
getIconSVG,
|
||||
@@ -8843,4 +8843,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=architectureDiagram-VXUJARFQ-7JNJRGGM.js.map
|
||||
//# sourceMappingURL=architectureDiagram-VXUJARFQ-WEBFVMSU.js.map
|
||||
+2
-2
@@ -4,10 +4,10 @@ import {
|
||||
import {
|
||||
clone_default
|
||||
} from "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
getIconStyles
|
||||
} from "./chunk-I4QIIVJ7.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
getLineFunctionsWithOffset
|
||||
} from "./chunk-2HSIUWWJ.js";
|
||||
@@ -3745,4 +3745,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=blockDiagram-VD42YOAC-6W666JF2.js.map
|
||||
//# sourceMappingURL=blockDiagram-VD42YOAC-KZTUA5HK.js.map
|
||||
+5
-5
@@ -12,14 +12,14 @@ import "./chunk-FNEVJCCX.js";
|
||||
import "./chunk-R33GOAXK.js";
|
||||
import "./chunk-5SXTVVUG.js";
|
||||
import "./chunk-WHHJWK6B.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import {
|
||||
setupViewPortForSVG
|
||||
} from "./chunk-LHH5RO5K.js";
|
||||
import {
|
||||
selectSvgElement
|
||||
} from "./chunk-B5NQPFQG.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
setupViewPortForSVG
|
||||
} from "./chunk-LHH5RO5K.js";
|
||||
import {
|
||||
isLabelStyle,
|
||||
styles2String
|
||||
@@ -566,4 +566,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=diagram-PSM6KHXK-3GNQQWOU.js.map
|
||||
//# sourceMappingURL=diagram-PSM6KHXK-OYEWFDFJ.js.map
|
||||
+2
-2
@@ -12,10 +12,10 @@ import "./chunk-FNEVJCCX.js";
|
||||
import "./chunk-R33GOAXK.js";
|
||||
import "./chunk-5SXTVVUG.js";
|
||||
import "./chunk-WHHJWK6B.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import {
|
||||
selectSvgElement
|
||||
} from "./chunk-B5NQPFQG.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
cleanAndMerge
|
||||
@@ -337,4 +337,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=diagram-QEK2KX5R-QLL2LDZJ.js.map
|
||||
//# sourceMappingURL=diagram-QEK2KX5R-KMPAAIQN.js.map
|
||||
+2
-2
@@ -12,10 +12,10 @@ import "./chunk-FNEVJCCX.js";
|
||||
import "./chunk-R33GOAXK.js";
|
||||
import "./chunk-5SXTVVUG.js";
|
||||
import "./chunk-WHHJWK6B.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import {
|
||||
selectSvgElement
|
||||
} from "./chunk-B5NQPFQG.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
cleanAndMerge
|
||||
@@ -247,4 +247,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=diagram-S2PKOQOG-RM7ASWFZ.js.map
|
||||
//# sourceMappingURL=diagram-S2PKOQOG-D6F7ANDO.js.map
|
||||
+2
-2
@@ -9,13 +9,13 @@ import "./chunk-FNEVJCCX.js";
|
||||
import "./chunk-R33GOAXK.js";
|
||||
import "./chunk-5SXTVVUG.js";
|
||||
import "./chunk-WHHJWK6B.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import {
|
||||
package_default
|
||||
} from "./chunk-BSULYXPT.js";
|
||||
import {
|
||||
selectSvgElement
|
||||
} from "./chunk-B5NQPFQG.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
configureSvgSize
|
||||
@@ -57,4 +57,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=infoDiagram-F6ZHWCRC-HMHRPPWW.js.map
|
||||
//# sourceMappingURL=infoDiagram-F6ZHWCRC-L22DC2WZ.js.map
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
getIconStyles
|
||||
} from "./chunk-I4QIIVJ7.js";
|
||||
import {
|
||||
selectSvgElement
|
||||
} from "./chunk-B5NQPFQG.js";
|
||||
import {
|
||||
getIconStyles
|
||||
} from "./chunk-I4QIIVJ7.js";
|
||||
import {
|
||||
JSON_SCHEMA,
|
||||
load
|
||||
@@ -1122,4 +1122,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=kanban-definition-3W4ZIXB7-GUMHX2OD.js.map
|
||||
//# sourceMappingURL=kanban-definition-3W4ZIXB7-H4KIKSB7.js.map
|
||||
+8
-8
@@ -522,7 +522,7 @@ var detector7 = __name((txt) => {
|
||||
return /^\s*info/.test(txt);
|
||||
}, "detector");
|
||||
var loader7 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./infoDiagram-F6ZHWCRC-HMHRPPWW.js");
|
||||
const { diagram: diagram2 } = await import("./infoDiagram-F6ZHWCRC-L22DC2WZ.js");
|
||||
return { id: id7, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var info = {
|
||||
@@ -535,7 +535,7 @@ var detector8 = __name((txt) => {
|
||||
return /^\s*pie/.test(txt);
|
||||
}, "detector");
|
||||
var loader8 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./pieDiagram-ADFJNKIX-HTPFO6AD.js");
|
||||
const { diagram: diagram2 } = await import("./pieDiagram-ADFJNKIX-QYG3H4AZ.js");
|
||||
return { id: id8, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var pie = {
|
||||
@@ -784,7 +784,7 @@ var detector21 = __name((txt) => {
|
||||
return /^\s*kanban/.test(txt);
|
||||
}, "detector");
|
||||
var loader21 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./kanban-definition-3W4ZIXB7-GUMHX2OD.js");
|
||||
const { diagram: diagram2 } = await import("./kanban-definition-3W4ZIXB7-H4KIKSB7.js");
|
||||
return { id: id21, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var plugin19 = {
|
||||
@@ -812,7 +812,7 @@ var detector23 = __name((txt) => {
|
||||
return /^\s*packet(-beta)?/.test(txt);
|
||||
}, "detector");
|
||||
var loader23 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./diagram-S2PKOQOG-RM7ASWFZ.js");
|
||||
const { diagram: diagram2 } = await import("./diagram-S2PKOQOG-D6F7ANDO.js");
|
||||
return { id: id23, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var packet = {
|
||||
@@ -825,7 +825,7 @@ var detector24 = __name((txt) => {
|
||||
return /^\s*radar-beta/.test(txt);
|
||||
}, "detector");
|
||||
var loader24 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./diagram-QEK2KX5R-QLL2LDZJ.js");
|
||||
const { diagram: diagram2 } = await import("./diagram-QEK2KX5R-KMPAAIQN.js");
|
||||
return { id: id24, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var radar = {
|
||||
@@ -838,7 +838,7 @@ var detector25 = __name((txt) => {
|
||||
return /^\s*block(-beta)?/.test(txt);
|
||||
}, "detector");
|
||||
var loader25 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./blockDiagram-VD42YOAC-6W666JF2.js");
|
||||
const { diagram: diagram2 } = await import("./blockDiagram-VD42YOAC-KZTUA5HK.js");
|
||||
return { id: id25, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var plugin21 = {
|
||||
@@ -852,7 +852,7 @@ var detector26 = __name((txt) => {
|
||||
return /^\s*architecture/.test(txt);
|
||||
}, "detector");
|
||||
var loader26 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./architectureDiagram-VXUJARFQ-7JNJRGGM.js");
|
||||
const { diagram: diagram2 } = await import("./architectureDiagram-VXUJARFQ-WEBFVMSU.js");
|
||||
return { id: id26, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var architecture = {
|
||||
@@ -866,7 +866,7 @@ var detector27 = __name((txt) => {
|
||||
return /^\s*treemap/.test(txt);
|
||||
}, "detector");
|
||||
var loader27 = __name(async () => {
|
||||
const { diagram: diagram2 } = await import("./diagram-PSM6KHXK-3GNQQWOU.js");
|
||||
const { diagram: diagram2 } = await import("./diagram-PSM6KHXK-OYEWFDFJ.js");
|
||||
return { id: id27, diagram: diagram2 };
|
||||
}, "loader");
|
||||
var treemap = {
|
||||
|
||||
+2
-2
@@ -12,10 +12,10 @@ import "./chunk-FNEVJCCX.js";
|
||||
import "./chunk-R33GOAXK.js";
|
||||
import "./chunk-5SXTVVUG.js";
|
||||
import "./chunk-WHHJWK6B.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import {
|
||||
selectSvgElement
|
||||
} from "./chunk-B5NQPFQG.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import {
|
||||
cleanAndMerge,
|
||||
@@ -225,4 +225,4 @@ var diagram = {
|
||||
export {
|
||||
diagram
|
||||
};
|
||||
//# sourceMappingURL=pieDiagram-ADFJNKIX-HTPFO6AD.js.map
|
||||
//# sourceMappingURL=pieDiagram-ADFJNKIX-QYG3H4AZ.js.map
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
Graph
|
||||
} from "./chunk-MEGNL3BT.js";
|
||||
import "./chunk-6SIVX7OU.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import "./chunk-PLWNSIKB.js";
|
||||
import "./chunk-LHH5RO5K.js";
|
||||
import "./chunk-NGEE2U2J.js";
|
||||
import "./chunk-SOVT3CA7.js";
|
||||
import "./chunk-ZCTBDDTS.js";
|
||||
import "./chunk-2HSIUWWJ.js";
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"hash": "0356a024",
|
||||
"hash": "c91a5572",
|
||||
"configHash": "9e47cd39",
|
||||
"lockfileHash": "77f35274",
|
||||
"browserHash": "24be409e",
|
||||
"lockfileHash": "610c0484",
|
||||
"browserHash": "db821900",
|
||||
"optimized": {},
|
||||
"chunks": {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
Generated
+16
@@ -9,6 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@angular/build": "^20.3.0",
|
||||
"@angular/cdk": "^20.2.4",
|
||||
"@angular/cli": "^20.3.0",
|
||||
"@angular/common": "^20.3.0",
|
||||
"@angular/compiler": "^20.3.0",
|
||||
@@ -514,6 +515,21 @@
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cdk": {
|
||||
"version": "20.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.5.tgz",
|
||||
"integrity": "sha512-1cpR/5jeKXLR1D+PsEvRn0QhSWD3/AjtbugJF5nlx/7L90YXhNFCmNAxAkdFKSn4YIDoPwMHgvOpS7yb51wohQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parse5": "^8.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^20.0.0 || ^21.0.0",
|
||||
"@angular/core": "^20.0.0 || ^21.0.0",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cli": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.2.tgz",
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
"dependencies": {
|
||||
"@angular/build": "^20.3.0",
|
||||
"@angular/cli": "^20.3.0",
|
||||
"@angular/cdk": "^20.2.4",
|
||||
"@angular/common": "^20.3.0",
|
||||
"@angular/compiler": "^20.3.0",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@angular/core": "^20.3.0",
|
||||
"@angular/forms": "^20.3.1",
|
||||
"@angular/platform-browser": "^20.3.0",
|
||||
"@angular/localize": "^20.3.0",
|
||||
"@types/markdown-it": "^14.0.1",
|
||||
"angular-calendar": "^0.32.0",
|
||||
"chokidar": "^4.0.3",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
<!-- ObsiViewer - Application optimisée pour mobile et desktop -->
|
||||
<main class="relative flex min-h-screen flex-col bg-obs-l-bg-main text-obs-l-text-main dark:bg-obs-d-bg-main dark:text-obs-d-text-main lg:flex-row lg:h-screen lg:overflow-hidden">
|
||||
@if (isRawViewOpen()) {
|
||||
<app-raw-view-overlay
|
||||
[content]="rawNoteContent()"
|
||||
[filename]="rawNoteFilename()"
|
||||
[wrap]="isRawViewWrapped()"
|
||||
(close)="closeRawView()"
|
||||
(toggleWrap)="toggleRawWrap()"
|
||||
></app-raw-view-overlay>
|
||||
}
|
||||
<!-- Navigation latérale desktop -->
|
||||
<nav class="hidden w-14 flex-col items-center gap-4 border-r border-obs-l-border bg-obs-l-bg-main py-4 dark:border-obs-d-border dark:bg-obs-d-bg-main lg:flex">
|
||||
<button
|
||||
@@ -318,6 +327,38 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden items-center gap-2 lg:flex">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary disabled:opacity-40 disabled:pointer-events-none"
|
||||
(click)="toggleRawView()"
|
||||
[disabled]="!selectedNote()"
|
||||
aria-label="Afficher le markdown brut"
|
||||
aria-keyshortcuts="Alt+R"
|
||||
title="Vue brute (Alt+R)"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
|
||||
<path d="M8.5 8 5 12l3.5 4" />
|
||||
<path d="M15.5 8 19 12l-3.5 4" />
|
||||
<path d="M12.5 6 11 18" />
|
||||
</svg>
|
||||
<span class="sr-only">Alt+R</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary disabled:opacity-40 disabled:pointer-events-none"
|
||||
(click)="downloadCurrentNote()"
|
||||
[disabled]="!selectedNote()"
|
||||
aria-label="Télécharger le fichier markdown"
|
||||
aria-keyshortcuts="Alt+D"
|
||||
title="Télécharger (Alt+D)"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
|
||||
<path d="M12 3v12" />
|
||||
<path d="M7 11l5 5 5-5" />
|
||||
<path d="M5 19h14" />
|
||||
</svg>
|
||||
<span class="sr-only">Alt+D</span>
|
||||
</button>
|
||||
<button
|
||||
(click)="toggleTheme()"
|
||||
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary"
|
||||
@@ -345,6 +386,38 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between lg:gap-6">
|
||||
<div class="flex items-center gap-2 lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary disabled:opacity-40 disabled:pointer-events-none"
|
||||
(click)="toggleRawView()"
|
||||
[disabled]="!selectedNote()"
|
||||
aria-label="Afficher le markdown brut"
|
||||
aria-keyshortcuts="Alt+R"
|
||||
title="Vue brute (Alt+R)"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
|
||||
<path d="M8.5 8 5 12l3.5 4" />
|
||||
<path d="M15.5 8 19 12l-3.5 4" />
|
||||
<path d="M12.5 6 11 18" />
|
||||
</svg>
|
||||
<span class="sr-only">Raccourci Alt+R</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary disabled:opacity-40 disabled:pointer-events-none"
|
||||
(click)="downloadCurrentNote()"
|
||||
[disabled]="!selectedNote()"
|
||||
aria-label="Télécharger le fichier markdown"
|
||||
aria-keyshortcuts="Alt+D"
|
||||
title="Télécharger (Alt+D)"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
|
||||
<path d="M12 3v12" />
|
||||
<path d="M7 11l5 5 5-5" />
|
||||
<path d="M5 19h14" />
|
||||
</svg>
|
||||
<span class="sr-only">Raccourci Alt+D</span>
|
||||
</button>
|
||||
<button
|
||||
(click)="toggleTheme()"
|
||||
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary"
|
||||
|
||||
+119
-2
@@ -1,9 +1,11 @@
|
||||
import { Component, ChangeDetectionStrategy, inject, signal, computed, effect, ElementRef, OnDestroy } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, HostListener, inject, signal, computed, effect, ElementRef, OnDestroy } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
// Services
|
||||
import { VaultService } from './services/vault.service';
|
||||
import { MarkdownService } from './services/markdown.service';
|
||||
import { MarkdownViewerService } from './services/markdown-viewer.service';
|
||||
import { DownloadService } from './core/services/download.service';
|
||||
|
||||
// Components
|
||||
import { FileExplorerComponent } from './components/file-explorer/file-explorer.component';
|
||||
@@ -11,6 +13,7 @@ import { NoteViewerComponent, WikiLinkActivation } from './components/note-viewe
|
||||
import { GraphViewComponent } from './components/graph-view/graph-view.component';
|
||||
import { TagsViewComponent } from './components/tags-view/tags-view.component';
|
||||
import { MarkdownCalendarComponent } from './components/markdown-calendar/markdown-calendar.component';
|
||||
import { RawViewOverlayComponent } from './shared/overlays/raw-view-overlay.component';
|
||||
|
||||
// Types
|
||||
import { FileMetadata, Note, TagInfo, VaultNode } from './types';
|
||||
@@ -31,6 +34,7 @@ interface TocEntry {
|
||||
GraphViewComponent,
|
||||
TagsViewComponent,
|
||||
MarkdownCalendarComponent,
|
||||
RawViewOverlayComponent,
|
||||
],
|
||||
templateUrl: './app.component.simple.html',
|
||||
styleUrls: ['./app.component.css'],
|
||||
@@ -39,6 +43,8 @@ interface TocEntry {
|
||||
export class AppComponent implements OnDestroy {
|
||||
private vaultService = inject(VaultService);
|
||||
private markdownService = inject(MarkdownService);
|
||||
private markdownViewerService = inject(MarkdownViewerService);
|
||||
private downloadService = inject(DownloadService);
|
||||
private elementRef = inject(ElementRef);
|
||||
|
||||
// --- State Signals ---
|
||||
@@ -51,10 +57,13 @@ export class AppComponent implements OnDestroy {
|
||||
tableOfContents = signal<TocEntry[]>([]);
|
||||
leftSidebarWidth = signal<number>(288);
|
||||
rightSidebarWidth = signal<number>(288);
|
||||
isRawViewOpen = signal<boolean>(false);
|
||||
isRawViewWrapped = signal<boolean>(true);
|
||||
readonly LEFT_MIN_WIDTH = 220;
|
||||
readonly LEFT_MAX_WIDTH = 520;
|
||||
readonly RIGHT_MIN_WIDTH = 220;
|
||||
readonly RIGHT_MAX_WIDTH = 520;
|
||||
private rawViewTriggerElement: HTMLElement | null = null;
|
||||
|
||||
private viewportWidth = signal<number>(typeof window !== 'undefined' ? window.innerWidth : 0);
|
||||
private resizeHandler = () => {
|
||||
@@ -98,6 +107,23 @@ export class AppComponent implements OnDestroy {
|
||||
return this.markdownService.render(note.content, allNotes, note);
|
||||
});
|
||||
|
||||
rawNoteContent = computed<string>(() => {
|
||||
const note = this.selectedNote();
|
||||
if (!note) {
|
||||
return '';
|
||||
}
|
||||
return note.rawContent ?? note.content ?? '';
|
||||
});
|
||||
|
||||
rawNoteFilename = computed<string>(() => {
|
||||
const note = this.selectedNote();
|
||||
if (!note) {
|
||||
return this.buildFallbackFilename();
|
||||
}
|
||||
const name = note.fileName?.trim();
|
||||
return name && name.length > 0 ? name : this.buildFallbackFilename();
|
||||
});
|
||||
|
||||
selectedNoteBreadcrumb = computed<string[]>(() => {
|
||||
const note = this.selectedNote();
|
||||
if (!note) {
|
||||
@@ -195,14 +221,22 @@ export class AppComponent implements OnDestroy {
|
||||
|
||||
// Effect to generate Table of Contents when the note changes
|
||||
effect(() => {
|
||||
const note = this.selectedNote();
|
||||
this.markdownViewerService.setCurrentNote(note ?? null);
|
||||
const html = this.renderedNoteContent();
|
||||
if (html && this.selectedNote()) {
|
||||
if (html && note) {
|
||||
this.generateToc(html);
|
||||
} else {
|
||||
this.tableOfContents.set([]);
|
||||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
if (!this.selectedNote()) {
|
||||
this.isRawViewOpen.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
const pending = this.pendingWikiNavigation();
|
||||
const activeNoteId = this.selectedNoteId();
|
||||
@@ -408,6 +442,7 @@ export class AppComponent implements OnDestroy {
|
||||
|
||||
this.vaultService.ensureFolderOpen(note.originalPath);
|
||||
this.selectedNoteId.set(note.id);
|
||||
this.markdownViewerService.setCurrentNote(note);
|
||||
|
||||
if (!this.isDesktopView() && this.activeView() === 'search') {
|
||||
this.isSidebarOpen.set(false);
|
||||
@@ -421,6 +456,88 @@ export class AppComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
toggleRawView(): void {
|
||||
if (!this.selectedNote()) {
|
||||
return;
|
||||
}
|
||||
if (this.isRawViewOpen()) {
|
||||
this.closeRawView();
|
||||
} else {
|
||||
this.openRawView();
|
||||
}
|
||||
}
|
||||
|
||||
openRawView(): void {
|
||||
if (!this.selectedNote()) {
|
||||
return;
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
this.rawViewTriggerElement = document.activeElement as HTMLElement | null;
|
||||
}
|
||||
this.isRawViewOpen.set(true);
|
||||
}
|
||||
|
||||
closeRawView(): void {
|
||||
this.isRawViewOpen.set(false);
|
||||
const target = this.rawViewTriggerElement;
|
||||
this.rawViewTriggerElement = null;
|
||||
if (target && typeof target.focus === 'function') {
|
||||
queueMicrotask(() => target.focus());
|
||||
}
|
||||
}
|
||||
|
||||
toggleRawWrap(): void {
|
||||
this.isRawViewWrapped.update(value => !value);
|
||||
}
|
||||
|
||||
downloadCurrentNote(): void {
|
||||
const note = this.selectedNote();
|
||||
if (!note) {
|
||||
return;
|
||||
}
|
||||
const filename = this.rawNoteFilename();
|
||||
const content = this.rawNoteContent();
|
||||
if (!content) {
|
||||
console.warn('[ObsiViewer] Aucun contenu à télécharger.');
|
||||
return;
|
||||
}
|
||||
this.downloadService.downloadText(content, filename);
|
||||
}
|
||||
|
||||
@HostListener('window:keydown', ['$event'])
|
||||
handleGlobalKeydown(event: KeyboardEvent): void {
|
||||
if (!event.altKey || event.repeat) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = event.key.toLowerCase();
|
||||
if (key === 'r') {
|
||||
if (!this.selectedNote()) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
this.toggleRawView();
|
||||
}
|
||||
if (key === 'd') {
|
||||
if (!this.selectedNote()) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
this.downloadCurrentNote();
|
||||
}
|
||||
}
|
||||
|
||||
private buildFallbackFilename(): string {
|
||||
const now = new Date();
|
||||
const pad = (value: number) => value.toString().padStart(2, '0');
|
||||
const year = now.getFullYear();
|
||||
const month = pad(now.getMonth() + 1);
|
||||
const day = pad(now.getDate());
|
||||
const hours = pad(now.getHours());
|
||||
const minutes = pad(now.getMinutes());
|
||||
return `note-${year}${month}${day}-${hours}${minutes}.md`;
|
||||
}
|
||||
|
||||
handleWikiLink(link: WikiLinkActivation): void {
|
||||
const target = link.target?.trim();
|
||||
if (!target) {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert/strict';
|
||||
import { DownloadService } from './download.service.js';
|
||||
|
||||
const makeFakeDocument = () => {
|
||||
const appendCalls: unknown[] = [];
|
||||
const removeCalls: unknown[] = [];
|
||||
const anchor = {
|
||||
href: '',
|
||||
download: '',
|
||||
rel: '',
|
||||
style: {} as Record<string, unknown>,
|
||||
clickCount: 0,
|
||||
click() {
|
||||
this.clickCount += 1;
|
||||
},
|
||||
};
|
||||
|
||||
const documentMock = {
|
||||
createElement(tag: string) {
|
||||
if (tag !== 'a') {
|
||||
throw new Error(`Unexpected element request: ${tag}`);
|
||||
}
|
||||
return anchor;
|
||||
},
|
||||
body: {
|
||||
appendChild(node: unknown) {
|
||||
appendCalls.push(node);
|
||||
},
|
||||
removeChild(node: unknown) {
|
||||
removeCalls.push(node);
|
||||
},
|
||||
},
|
||||
} as unknown as Document;
|
||||
|
||||
return { documentMock, anchor, appendCalls, removeCalls };
|
||||
};
|
||||
|
||||
test('DownloadService downloads normalized UTF-8 content', async () => {
|
||||
const service = new DownloadService();
|
||||
const originalDocument = globalThis.document;
|
||||
const NativeURL = globalThis.URL;
|
||||
|
||||
const { documentMock, anchor, appendCalls, removeCalls } = makeFakeDocument();
|
||||
let lastBlob: Blob | null = null;
|
||||
const revoked: string[] = [];
|
||||
|
||||
const urlMock = Object.assign(
|
||||
function MockURL(url: string | URL, base?: string | URL) {
|
||||
return new NativeURL(url as string, base as string);
|
||||
},
|
||||
{
|
||||
prototype: NativeURL.prototype,
|
||||
canParse: () => true,
|
||||
parse: (url: string | URL, base?: string | URL) => new NativeURL(url as string, base as string),
|
||||
createObjectURL(blob: Blob) {
|
||||
lastBlob = blob;
|
||||
return 'blob:test-url';
|
||||
},
|
||||
revokeObjectURL(url: string) {
|
||||
revoked.push(url);
|
||||
},
|
||||
}
|
||||
) as unknown as typeof URL;
|
||||
|
||||
globalThis.document = documentMock;
|
||||
globalThis.URL = urlMock;
|
||||
|
||||
try {
|
||||
service.downloadText('line1\r\nline2', 'note.md');
|
||||
|
||||
assert.equal(anchor.download, 'note.md');
|
||||
assert.equal(anchor.rel, 'noopener');
|
||||
assert.equal(anchor.clickCount, 1);
|
||||
assert.equal(appendCalls.length, 1);
|
||||
assert.equal(removeCalls.length, 1);
|
||||
assert.equal(revoked.length, 1);
|
||||
assert.ok(lastBlob, 'Blob should be created');
|
||||
const blobContent = await lastBlob!.text();
|
||||
assert.equal(blobContent, 'line1\nline2');
|
||||
} finally {
|
||||
globalThis.document = originalDocument;
|
||||
globalThis.URL = NativeURL;
|
||||
}
|
||||
});
|
||||
|
||||
test('DownloadService warns when content is empty', () => {
|
||||
const service = new DownloadService();
|
||||
const originalDocument = globalThis.document;
|
||||
const NativeURL = globalThis.URL;
|
||||
const originalWarn = console.warn;
|
||||
|
||||
const { documentMock } = makeFakeDocument();
|
||||
const urlMock = Object.assign(
|
||||
function MockURL(url: string | URL, base?: string | URL) {
|
||||
return new NativeURL(url as string, base as string);
|
||||
},
|
||||
{
|
||||
prototype: NativeURL.prototype,
|
||||
canParse: () => true,
|
||||
parse: (url: string | URL, base?: string | URL) => new NativeURL(url as string, base as string),
|
||||
createObjectURL() {
|
||||
return 'blob:noop';
|
||||
},
|
||||
revokeObjectURL() {
|
||||
// noop
|
||||
},
|
||||
}
|
||||
) as unknown as typeof URL;
|
||||
|
||||
globalThis.document = documentMock;
|
||||
globalThis.URL = urlMock;
|
||||
|
||||
let warned = false;
|
||||
console.warn = () => {
|
||||
warned = true;
|
||||
};
|
||||
|
||||
try {
|
||||
service.downloadText('', 'empty.md');
|
||||
assert.equal(warned, true);
|
||||
} finally {
|
||||
console.warn = originalWarn;
|
||||
globalThis.document = originalDocument;
|
||||
globalThis.URL = NativeURL;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DownloadService {
|
||||
downloadText(content: string, filename: string, mime = 'text/markdown;charset=utf-8'): void {
|
||||
if (!content) {
|
||||
console.warn('[DownloadService] Empty content provided for download.');
|
||||
}
|
||||
|
||||
const normalized = content.replace(/\r\n/g, '\n');
|
||||
const blob = new Blob([normalized], { type: mime });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
try {
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
link.rel = 'noopener';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} finally {
|
||||
setTimeout(() => URL.revokeObjectURL(url), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert/strict';
|
||||
import { firstValueFrom, skip, take } from 'rxjs';
|
||||
import { MarkdownViewerService } from './markdown-viewer.service.js';
|
||||
import type { Note } from '../../types.js';
|
||||
|
||||
test('MarkdownViewerService exposes current note streams', async () => {
|
||||
const service = new MarkdownViewerService();
|
||||
|
||||
const note: Note = {
|
||||
id: 'note-1',
|
||||
title: 'Test note',
|
||||
content: 'rendered content',
|
||||
rawContent: 'raw markdown',
|
||||
tags: [],
|
||||
frontmatter: {},
|
||||
backlinks: [],
|
||||
mtime: Date.now(),
|
||||
fileName: 'note-1.md',
|
||||
filePath: '/note-1.md',
|
||||
originalPath: 'note-1',
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
};
|
||||
|
||||
service.setCurrentNote(note);
|
||||
|
||||
const observedNote = await firstValueFrom(service.getCurrentNote$().pipe(skip(1), take(1)));
|
||||
const observedRaw = await firstValueFrom(service.getCurrentRaw$().pipe(skip(1), take(1)));
|
||||
const observedFilename = await firstValueFrom(service.getCurrentFilename$().pipe(skip(1), take(1)));
|
||||
|
||||
assert.equal(observedNote?.id, note.id);
|
||||
assert.equal(observedRaw, note.rawContent);
|
||||
assert.equal(observedFilename, note.fileName);
|
||||
});
|
||||
|
||||
test('MarkdownViewerService falls back to content when rawContent missing', async () => {
|
||||
const service = new MarkdownViewerService();
|
||||
|
||||
const note: Note = {
|
||||
id: 'note-2',
|
||||
title: 'Another note',
|
||||
content: 'fallback content',
|
||||
rawContent: undefined as unknown as string,
|
||||
tags: [],
|
||||
frontmatter: {},
|
||||
backlinks: [],
|
||||
mtime: Date.now(),
|
||||
fileName: 'note-2.md',
|
||||
filePath: '/note-2.md',
|
||||
originalPath: 'note-2',
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
};
|
||||
|
||||
service.setCurrentNote(note);
|
||||
|
||||
const observedRaw = await firstValueFrom(service.getCurrentRaw$().pipe(skip(1), take(1)));
|
||||
assert.equal(observedRaw, note.content);
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { Note } from '../../types';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MarkdownViewerService {
|
||||
private readonly currentNoteSubject = new BehaviorSubject<Note | null>(null);
|
||||
private readonly currentRawSubject = new BehaviorSubject<string>('');
|
||||
private readonly currentFilenameSubject = new BehaviorSubject<string | null>(null);
|
||||
|
||||
getCurrentNote$(): Observable<Note | null> {
|
||||
return this.currentNoteSubject.asObservable();
|
||||
}
|
||||
|
||||
getCurrentRaw$(): Observable<string> {
|
||||
return this.currentRawSubject.asObservable();
|
||||
}
|
||||
|
||||
getCurrentFilename$(): Observable<string | null> {
|
||||
return this.currentFilenameSubject.asObservable();
|
||||
}
|
||||
|
||||
setCurrentNote(note: Note | null): void {
|
||||
if (!note) {
|
||||
this.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentNoteSubject.next(note);
|
||||
const raw = typeof note.rawContent === 'string' ? note.rawContent : note.content;
|
||||
this.currentRawSubject.next(raw ?? '');
|
||||
this.currentFilenameSubject.next(note.fileName ?? null);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.currentNoteSubject.next(null);
|
||||
this.currentRawSubject.next('');
|
||||
this.currentFilenameSubject.next(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { MarkdownViewerService } from '../core/services/markdown-viewer.service';
|
||||
@@ -237,7 +237,8 @@ export class VaultService implements OnDestroy {
|
||||
let detectedHomeVaultName: string | null = null;
|
||||
|
||||
for (const apiNote of notes) {
|
||||
const { frontmatter, body } = this.parseFrontmatter(apiNote.content);
|
||||
const normalizedRaw = apiNote.content.replace(/\r\n/g, '\n');
|
||||
const { frontmatter, body } = this.parseFrontmatter(normalizedRaw);
|
||||
const derivedTitle = this.extractTitle(body, apiNote.id);
|
||||
const noteTitle = frontmatter.title || apiNote.title || derivedTitle;
|
||||
|
||||
@@ -265,6 +266,7 @@ export class VaultService implements OnDestroy {
|
||||
id: apiNote.id,
|
||||
title: noteTitle,
|
||||
content: body,
|
||||
rawContent: normalizedRaw,
|
||||
tags: Array.from(tagSet),
|
||||
frontmatter,
|
||||
backlinks: [],
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<div class="raw-overlay__backdrop" (click)="onBackdropClick()">
|
||||
<div
|
||||
class="raw-overlay__panel"
|
||||
cdkTrapFocus
|
||||
[cdkTrapFocusAutoCapture]="true"
|
||||
(click)="onPanelClick($event)"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
[attr.aria-labelledby]="headingId"
|
||||
>
|
||||
<header class="raw-overlay__header">
|
||||
<div class="raw-overlay__title-group">
|
||||
<h2 class="raw-overlay__title" [id]="headingId">Vue brute</h2>
|
||||
<p class="raw-overlay__subtitle" *ngIf="filename">{{ filename }}</p>
|
||||
</div>
|
||||
<div class="raw-overlay__actions">
|
||||
<button
|
||||
type="button"
|
||||
class="raw-overlay__action"
|
||||
(click)="copyAll()"
|
||||
aria-label="Copier tout"
|
||||
>
|
||||
Copier tout
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="raw-overlay__action"
|
||||
(click)="onToggleWrap()"
|
||||
[attr.aria-pressed]="wrap"
|
||||
[attr.aria-label]="wrapButtonAriaLabel"
|
||||
>
|
||||
@if (wrap) {
|
||||
<span>Retour auto désactivé</span>
|
||||
} @else {
|
||||
<span>Retour auto activé</span>
|
||||
}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="raw-overlay__action raw-overlay__action--primary"
|
||||
(click)="close.emit()"
|
||||
aria-label="Fermer la vue brute"
|
||||
>
|
||||
Fermer
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="raw-overlay__feedback" *ngIf="feedback() as message" role="status" aria-live="polite">
|
||||
{{ message }}
|
||||
</div>
|
||||
|
||||
<section
|
||||
class="raw-overlay__content"
|
||||
[class.raw-overlay__content--pre-wrap]="wrap"
|
||||
tabindex="0"
|
||||
aria-label="Zone de contenu markdown brut"
|
||||
>
|
||||
<pre>{{ content }}</pre>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,187 @@
|
||||
@use 'sass:color';
|
||||
|
||||
:host {
|
||||
--raw-overlay-bg: #f7fdfb;
|
||||
--raw-overlay-border: rgba(14, 116, 144, 0.18);
|
||||
--raw-overlay-text: #0f172a;
|
||||
--raw-overlay-accent: rgba(14, 116, 144, 0.14);
|
||||
--raw-overlay-accent-strong: rgba(14, 116, 144, 0.22);
|
||||
--raw-overlay-action-text: #0f766e;
|
||||
--raw-overlay-action-border: rgba(13, 148, 136, 0.45);
|
||||
--raw-overlay-action-bg: rgba(13, 148, 136, 0.12);
|
||||
--raw-overlay-action-bg-hover: rgba(13, 148, 136, 0.2);
|
||||
--raw-overlay-action-border-hover: rgba(13, 148, 136, 0.6);
|
||||
--raw-overlay-primary-bg: rgba(21, 128, 61, 0.18);
|
||||
--raw-overlay-primary-border: rgba(21, 128, 61, 0.55);
|
||||
--raw-overlay-primary-bg-hover: rgba(21, 128, 61, 0.28);
|
||||
--raw-overlay-primary-text: #166534;
|
||||
--raw-overlay-primary-border-hover: rgba(21, 128, 61, 0.7);
|
||||
}
|
||||
|
||||
:host-context(.dark) {
|
||||
--raw-overlay-bg: rgba(32, 33, 35, 0.96); /* #202123 */
|
||||
--raw-overlay-border: rgba(99, 102, 106, 0.35);
|
||||
--raw-overlay-text: #e5e7eb;
|
||||
--raw-overlay-accent: rgba(45, 46, 48, 0.88); /* #2D2E30 */
|
||||
--raw-overlay-accent-strong: rgba(61, 62, 65, 0.92);
|
||||
--raw-overlay-action-text: #34d399;
|
||||
--raw-overlay-action-border: rgba(52, 211, 153, 0.35);
|
||||
--raw-overlay-action-bg: rgba(12, 84, 68, 0.32);
|
||||
--raw-overlay-action-bg-hover: rgba(16, 185, 129, 0.36);
|
||||
--raw-overlay-action-border-hover: rgba(52, 211, 153, 0.55);
|
||||
--raw-overlay-primary-bg: rgba(22, 163, 74, 0.32);
|
||||
--raw-overlay-primary-border: rgba(22, 163, 74, 0.55);
|
||||
--raw-overlay-primary-bg-hover: rgba(22, 163, 74, 0.42);
|
||||
--raw-overlay-primary-text: #bbf7d0;
|
||||
--raw-overlay-primary-border-hover: rgba(22, 163, 74, 0.75);
|
||||
}
|
||||
|
||||
.raw-overlay__backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 60;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: clamp(1rem, 2vw, 2rem);
|
||||
background-color: rgba(15, 23, 42, 0.55);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.raw-overlay__panel {
|
||||
width: min(960px, calc(100vw - clamp(2rem, 4vw, 4rem)));
|
||||
max-height: calc(100vh - clamp(2rem, 4vw, 4rem));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 1.25rem;
|
||||
border: 1px solid var(--raw-overlay-border);
|
||||
background: var(--raw-overlay-bg);
|
||||
color: var(--raw-overlay-text);
|
||||
box-shadow: 0 28px 52px -16px rgba(15, 23, 42, 0.55);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.raw-overlay__header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
padding: 1.5rem clamp(1.25rem, 2vw, 2rem);
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
||||
}
|
||||
|
||||
.raw-overlay__title-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.raw-overlay__title {
|
||||
margin: 0;
|
||||
font-size: clamp(1.25rem, 2vw, 1.5rem);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.raw-overlay__subtitle {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.raw-overlay__actions {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.65rem;
|
||||
}
|
||||
|
||||
.raw-overlay__action {
|
||||
appearance: none;
|
||||
border: 1px solid var(--raw-overlay-action-border);
|
||||
border-radius: 9999px;
|
||||
padding: 0.45rem 1.1rem;
|
||||
background: var(--raw-overlay-action-bg);
|
||||
color: var(--raw-overlay-action-text);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
|
||||
}
|
||||
|
||||
.raw-overlay__action:hover,
|
||||
.raw-overlay__action:focus-visible {
|
||||
border-color: var(--raw-overlay-action-border-hover);
|
||||
background: var(--raw-overlay-action-bg-hover);
|
||||
color: var(--raw-overlay-action-text);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.raw-overlay__action--primary {
|
||||
background: var(--raw-overlay-primary-bg);
|
||||
border-color: var(--raw-overlay-primary-border);
|
||||
color: var(--raw-overlay-primary-text);
|
||||
}
|
||||
|
||||
.raw-overlay__action--primary:hover,
|
||||
.raw-overlay__action--primary:focus-visible {
|
||||
background: var(--raw-overlay-primary-bg-hover);
|
||||
border-color: var(--raw-overlay-primary-border-hover);
|
||||
}
|
||||
|
||||
.raw-overlay__feedback {
|
||||
margin: 0.75rem clamp(1.25rem, 2vw, 2rem) 0;
|
||||
padding: 0.6rem 1rem;
|
||||
border-radius: 0.75rem;
|
||||
background: rgba(21, 128, 61, 0.18);
|
||||
color: rgba(22, 163, 74, 0.9);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:host-context(.dark) .raw-overlay__feedback {
|
||||
background: rgba(34, 197, 94, 0.25);
|
||||
color: rgba(190, 242, 100, 0.95);
|
||||
}
|
||||
|
||||
.raw-overlay__content {
|
||||
flex: 1;
|
||||
margin: clamp(1rem, 2vw, 1.5rem) clamp(1.25rem, 2vw, 2rem) clamp(1.5rem, 3vw, 2rem);
|
||||
padding: clamp(1rem, 2vw, 1.5rem);
|
||||
border-radius: 1rem;
|
||||
background: var(--raw-overlay-accent);
|
||||
border: 1px solid var(--raw-overlay-border);
|
||||
overflow: auto;
|
||||
font-family: ui-monospace, SFMono-Regular, SFMono, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.45;
|
||||
white-space: pre;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.raw-overlay__content pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.raw-overlay__content--pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:host-context(.dark) .raw-overlay__content {
|
||||
background: var(--raw-overlay-accent);
|
||||
border-color: var(--raw-overlay-accent-strong);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.raw-overlay__panel {
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.raw-overlay__actions {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.raw-overlay__content {
|
||||
max-height: calc(100vh - 14rem);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert/strict';
|
||||
import { RawViewOverlayComponent } from './raw-view-overlay.component.js';
|
||||
|
||||
test('wrap button aria label reflects current state', () => {
|
||||
const component = new RawViewOverlayComponent();
|
||||
component.wrap = true;
|
||||
assert.equal(component.wrapButtonAriaLabel, 'Désactiver le retour automatique à la ligne');
|
||||
|
||||
component.wrap = false;
|
||||
assert.equal(component.wrapButtonAriaLabel, 'Activer le retour automatique à la ligne');
|
||||
});
|
||||
|
||||
test('toggle wrap emits through output', () => {
|
||||
const component = new RawViewOverlayComponent();
|
||||
let emitted = false;
|
||||
component.toggleWrap.subscribe(() => {
|
||||
emitted = true;
|
||||
});
|
||||
|
||||
component.onToggleWrap();
|
||||
assert.equal(emitted, true);
|
||||
});
|
||||
|
||||
test('close event emits on backdrop click and Esc key', () => {
|
||||
const component = new RawViewOverlayComponent();
|
||||
let closeCount = 0;
|
||||
component.close.subscribe(() => {
|
||||
closeCount += 1;
|
||||
});
|
||||
|
||||
component.onBackdropClick();
|
||||
component.handleKeydown(new KeyboardEvent('keydown', { key: 'Escape' }));
|
||||
assert.equal(closeCount, 2);
|
||||
});
|
||||
|
||||
test('copyAll uses clipboard when available', async () => {
|
||||
const component = new RawViewOverlayComponent();
|
||||
component.content = 'Example content';
|
||||
|
||||
const originalNavigator = globalThis.navigator;
|
||||
let captured = '';
|
||||
const mockNavigator = Object.assign(
|
||||
Object.create(originalNavigator ? Object.getPrototypeOf(originalNavigator) : {}),
|
||||
originalNavigator ?? {},
|
||||
{
|
||||
clipboard: {
|
||||
writeText: async (text: string) => {
|
||||
captured = text;
|
||||
},
|
||||
},
|
||||
},
|
||||
) as Navigator;
|
||||
|
||||
Object.defineProperty(globalThis, 'navigator', {
|
||||
configurable: true,
|
||||
value: mockNavigator,
|
||||
});
|
||||
|
||||
try {
|
||||
await component.copyAll();
|
||||
assert.equal(captured, 'Example content');
|
||||
assert.equal(component.feedback(), component.copySuccessMessage);
|
||||
} finally {
|
||||
if (originalNavigator) {
|
||||
Object.defineProperty(globalThis, 'navigator', {
|
||||
configurable: true,
|
||||
value: originalNavigator,
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
delete (globalThis as any).navigator;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CdkTrapFocus } from '@angular/cdk/a11y';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input, OnDestroy, Output, signal } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-raw-view-overlay',
|
||||
standalone: true,
|
||||
imports: [CommonModule, CdkTrapFocus],
|
||||
templateUrl: './raw-view-overlay.component.html',
|
||||
styleUrls: ['./raw-view-overlay.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RawViewOverlayComponent implements OnDestroy {
|
||||
@Input({ required: true }) content = '';
|
||||
@Input() filename: string | null = null;
|
||||
@Input() wrap = true;
|
||||
|
||||
@Output() close = new EventEmitter<void>();
|
||||
@Output() toggleWrap = new EventEmitter<void>();
|
||||
|
||||
readonly feedback = signal<string | null>(null);
|
||||
|
||||
private feedbackTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
readonly headingId = 'raw-view-overlay-title';
|
||||
readonly copySuccessMessage = 'Copié dans le presse-papiers';
|
||||
readonly copyErrorMessage = 'Copie impossible';
|
||||
private readonly wrapDisableLabel = 'Désactiver le retour automatique à la ligne';
|
||||
private readonly wrapEnableLabel = 'Activer le retour automatique à la ligne';
|
||||
|
||||
get wrapButtonAriaLabel(): string {
|
||||
return this.wrap ? this.wrapDisableLabel : this.wrapEnableLabel;
|
||||
}
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
handleKeydown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
this.close.emit();
|
||||
}
|
||||
}
|
||||
|
||||
onBackdropClick(): void {
|
||||
this.close.emit();
|
||||
}
|
||||
|
||||
onPanelClick(event: MouseEvent): void {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
onToggleWrap(): void {
|
||||
this.toggleWrap.emit();
|
||||
}
|
||||
|
||||
async copyAll(): Promise<void> {
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(this.content);
|
||||
} else {
|
||||
this.fallbackCopy(this.content);
|
||||
}
|
||||
this.showFeedback(this.copySuccessMessage);
|
||||
} catch (error) {
|
||||
console.error('[RawViewOverlay] Unable to copy content', error);
|
||||
this.showFeedback(this.copyErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.feedbackTimer) {
|
||||
clearTimeout(this.feedbackTimer);
|
||||
this.feedbackTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private fallbackCopy(text: string): void {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
textarea.style.pointerEvents = 'none';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.focus();
|
||||
textarea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
|
||||
private showFeedback(message: string): void {
|
||||
this.feedback.set(message);
|
||||
if (this.feedbackTimer) {
|
||||
clearTimeout(this.feedbackTimer);
|
||||
}
|
||||
this.feedbackTimer = setTimeout(() => {
|
||||
this.feedback.set(null);
|
||||
this.feedbackTimer = null;
|
||||
}, 2400);
|
||||
}
|
||||
}
|
||||
@@ -163,6 +163,18 @@
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&:root {
|
||||
--external-link: #6366f1;
|
||||
--external-link-hover: #4338ca;
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface Note {
|
||||
id: string;
|
||||
title: string;
|
||||
content: string;
|
||||
rawContent: string;
|
||||
tags: string[];
|
||||
frontmatter: { [key: string]: any };
|
||||
backlinks: string[];
|
||||
|
||||
Reference in New Issue
Block a user