chore: update TypeScript build info cache
This commit is contained in:
parent
a8b60f87f6
commit
1c7a49fd0e
2
.angular/cache/20.3.2/app/.tsbuildinfo
vendored
2
.angular/cache/20.3.2/app/.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
3288
.angular/cache/20.3.2/app/vite/deps/@angular_cdk_a11y.js
vendored
Normal file
3288
.angular/cache/20.3.2/app/vite/deps/@angular_cdk_a11y.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
.angular/cache/20.3.2/app/vite/deps/@angular_cdk_a11y.js.map
vendored
Normal file
7
.angular/cache/20.3.2/app/vite/deps/@angular_cdk_a11y.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
178
.angular/cache/20.3.2/app/vite/deps/_metadata.json
vendored
178
.angular/cache/20.3.2/app/vite/deps/_metadata.json
vendored
@ -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"
|
||||
},
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
16
.angular/cache/20.3.2/app/vite/deps/mermaid.js
vendored
16
.angular/cache/20.3.2/app/vite/deps/mermaid.js
vendored
@ -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 = {
|
||||
|
@ -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";
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"hash": "0356a024",
|
||||
"hash": "c91a5572",
|
||||
"configHash": "9e47cd39",
|
||||
"lockfileHash": "77f35274",
|
||||
"browserHash": "24be409e",
|
||||
"lockfileHash": "610c0484",
|
||||
"browserHash": "db821900",
|
||||
"optimized": {},
|
||||
"chunks": {}
|
||||
}
|
3
.angular/cache/20.3.2/app/vite/deps_temp_bb671c39/package.json
vendored
Normal file
3
.angular/cache/20.3.2/app/vite/deps_temp_bb671c39/package.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -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"
|
||||
|
@ -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) {
|
||||
|
127
src/core/services/download.service.spec.ts
Normal file
127
src/core/services/download.service.spec.ts
Normal file
@ -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;
|
||||
}
|
||||
});
|
29
src/core/services/download.service.ts
Normal file
29
src/core/services/download.service.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
60
src/core/services/markdown-viewer.service.spec.ts
Normal file
60
src/core/services/markdown-viewer.service.spec.ts
Normal file
@ -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);
|
||||
});
|
42
src/core/services/markdown-viewer.service.ts
Normal file
42
src/core/services/markdown-viewer.service.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
1
src/services/markdown-viewer.service.ts
Normal file
1
src/services/markdown-viewer.service.ts
Normal file
@ -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: [],
|
||||
|
62
src/shared/overlays/raw-view-overlay.component.html
Normal file
62
src/shared/overlays/raw-view-overlay.component.html
Normal file
@ -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>
|
187
src/shared/overlays/raw-view-overlay.component.scss
Normal file
187
src/shared/overlays/raw-view-overlay.component.scss
Normal file
@ -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);
|
||||
}
|
||||
}
|
75
src/shared/overlays/raw-view-overlay.component.spec.ts
Normal file
75
src/shared/overlays/raw-view-overlay.component.spec.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
101
src/shared/overlays/raw-view-overlay.component.ts
Normal file
101
src/shared/overlays/raw-view-overlay.component.ts
Normal file
@ -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[];
|
||||
|
Loading…
x
Reference in New Issue
Block a user