diff --git a/src/main/ts/index.html b/src/main/ts/index.html new file mode 100644 index 000000000..bc4f23150 --- /dev/null +++ b/src/main/ts/index.html @@ -0,0 +1,17 @@ + + + + + + Vite + TS + + +
+ 拖拽文件进入此处 +
+
+ +
+ + + diff --git a/src/main/ts/package.json b/src/main/ts/package.json new file mode 100644 index 000000000..54e59979f --- /dev/null +++ b/src/main/ts/package.json @@ -0,0 +1,21 @@ +{ + "name": "test-ts3", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + "typescript": "^5.0.2", + "vite": "^4.3.2" + }, + "dependencies": { + "gojs": "^2.3.7", + "js-yaml": "^4.1.0", + "yaml": "^2.3.0-5" + } +} diff --git a/src/main/ts/pnpm-lock.yaml b/src/main/ts/pnpm-lock.yaml new file mode 100644 index 000000000..fa8a86b2c --- /dev/null +++ b/src/main/ts/pnpm-lock.yaml @@ -0,0 +1,427 @@ +lockfileVersion: '6.0' + +dependencies: + gojs: + specifier: ^2.3.7 + version: registry.npmmirror.com/gojs@2.3.7 + js-yaml: + specifier: ^4.1.0 + version: registry.npmmirror.com/js-yaml@4.1.0 + yaml: + specifier: ^2.3.0-5 + version: registry.npmmirror.com/yaml@2.3.0-5 + +devDependencies: + '@types/js-yaml': + specifier: ^4.0.5 + version: registry.npmmirror.com/@types/js-yaml@4.0.5 + typescript: + specifier: ^5.0.2 + version: registry.npmmirror.com/typescript@5.0.2 + vite: + specifier: ^4.3.2 + version: registry.npmmirror.com/vite@4.3.2 + +packages: + + registry.npmmirror.com/@esbuild/android-arm64@0.17.19: + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz} + name: '@esbuild/android-arm64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/android-arm@0.17.19: + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz} + name: '@esbuild/android-arm' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/android-x64@0.17.19: + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz} + name: '@esbuild/android-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/darwin-arm64@0.17.19: + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz} + name: '@esbuild/darwin-arm64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/darwin-x64@0.17.19: + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz} + name: '@esbuild/darwin-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/freebsd-arm64@0.17.19: + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz} + name: '@esbuild/freebsd-arm64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/freebsd-x64@0.17.19: + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz} + name: '@esbuild/freebsd-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-arm64@0.17.19: + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz} + name: '@esbuild/linux-arm64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-arm@0.17.19: + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz} + name: '@esbuild/linux-arm' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-ia32@0.17.19: + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz} + name: '@esbuild/linux-ia32' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-loong64@0.17.19: + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz} + name: '@esbuild/linux-loong64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-mips64el@0.17.19: + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz} + name: '@esbuild/linux-mips64el' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-ppc64@0.17.19: + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz} + name: '@esbuild/linux-ppc64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-riscv64@0.17.19: + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz} + name: '@esbuild/linux-riscv64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz} + name: '@esbuild/linux-s390x' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/linux-x64@0.17.19: + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz} + name: '@esbuild/linux-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/netbsd-x64@0.17.19: + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz} + name: '@esbuild/netbsd-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/openbsd-x64@0.17.19: + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz} + name: '@esbuild/openbsd-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/sunos-x64@0.17.19: + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz} + name: '@esbuild/sunos-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/win32-arm64@0.17.19: + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz} + name: '@esbuild/win32-arm64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/win32-ia32@0.17.19: + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz} + name: '@esbuild/win32-ia32' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@esbuild/win32-x64@0.17.19: + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz} + name: '@esbuild/win32-x64' + version: 0.17.19 + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/@types/js-yaml@4.0.5: + resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/js-yaml/-/js-yaml-4.0.5.tgz} + name: '@types/js-yaml' + version: 4.0.5 + dev: true + + registry.npmmirror.com/argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz} + name: argparse + version: 2.0.1 + dev: false + + registry.npmmirror.com/esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild/-/esbuild-0.17.19.tgz} + name: esbuild + version: 0.17.19 + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': registry.npmmirror.com/@esbuild/android-arm@0.17.19 + '@esbuild/android-arm64': registry.npmmirror.com/@esbuild/android-arm64@0.17.19 + '@esbuild/android-x64': registry.npmmirror.com/@esbuild/android-x64@0.17.19 + '@esbuild/darwin-arm64': registry.npmmirror.com/@esbuild/darwin-arm64@0.17.19 + '@esbuild/darwin-x64': registry.npmmirror.com/@esbuild/darwin-x64@0.17.19 + '@esbuild/freebsd-arm64': registry.npmmirror.com/@esbuild/freebsd-arm64@0.17.19 + '@esbuild/freebsd-x64': registry.npmmirror.com/@esbuild/freebsd-x64@0.17.19 + '@esbuild/linux-arm': registry.npmmirror.com/@esbuild/linux-arm@0.17.19 + '@esbuild/linux-arm64': registry.npmmirror.com/@esbuild/linux-arm64@0.17.19 + '@esbuild/linux-ia32': registry.npmmirror.com/@esbuild/linux-ia32@0.17.19 + '@esbuild/linux-loong64': registry.npmmirror.com/@esbuild/linux-loong64@0.17.19 + '@esbuild/linux-mips64el': registry.npmmirror.com/@esbuild/linux-mips64el@0.17.19 + '@esbuild/linux-ppc64': registry.npmmirror.com/@esbuild/linux-ppc64@0.17.19 + '@esbuild/linux-riscv64': registry.npmmirror.com/@esbuild/linux-riscv64@0.17.19 + '@esbuild/linux-s390x': registry.npmmirror.com/@esbuild/linux-s390x@0.17.19 + '@esbuild/linux-x64': registry.npmmirror.com/@esbuild/linux-x64@0.17.19 + '@esbuild/netbsd-x64': registry.npmmirror.com/@esbuild/netbsd-x64@0.17.19 + '@esbuild/openbsd-x64': registry.npmmirror.com/@esbuild/openbsd-x64@0.17.19 + '@esbuild/sunos-x64': registry.npmmirror.com/@esbuild/sunos-x64@0.17.19 + '@esbuild/win32-arm64': registry.npmmirror.com/@esbuild/win32-arm64@0.17.19 + '@esbuild/win32-ia32': registry.npmmirror.com/@esbuild/win32-ia32@0.17.19 + '@esbuild/win32-x64': registry.npmmirror.com/@esbuild/win32-x64@0.17.19 + dev: true + + registry.npmmirror.com/fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz} + name: fsevents + version: 2.3.2 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + registry.npmmirror.com/gojs@2.3.7: + resolution: {integrity: sha512-UX/2G8IeU4GErAo3ZCtkSpTJ6xvIqr5TOoJGjPLJ4DRukDweVk78scsE99wohZC2oqDOIrKEathUmHiskRGong==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/gojs/-/gojs-2.3.7.tgz} + name: gojs + version: 2.3.7 + dev: false + + registry.npmmirror.com/js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz} + name: js-yaml + version: 4.1.0 + hasBin: true + dependencies: + argparse: registry.npmmirror.com/argparse@2.0.1 + dev: false + + registry.npmmirror.com/nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz} + name: nanoid + version: 3.3.6 + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + registry.npmmirror.com/picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz} + name: picocolors + version: 1.0.0 + dev: true + + registry.npmmirror.com/postcss@8.4.23: + resolution: {integrity: sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/postcss/-/postcss-8.4.23.tgz} + name: postcss + version: 8.4.23 + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: registry.npmmirror.com/nanoid@3.3.6 + picocolors: registry.npmmirror.com/picocolors@1.0.0 + source-map-js: registry.npmmirror.com/source-map-js@1.0.2 + dev: true + + registry.npmmirror.com/rollup@3.22.0: + resolution: {integrity: sha512-imsigcWor5Y/dC0rz2q0bBt9PabcL3TORry2hAa6O6BuMvY71bqHyfReAz5qyAqiQATD1m70qdntqBfBQjVWpQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/rollup/-/rollup-3.22.0.tgz} + name: rollup + version: 3.22.0 + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: registry.npmmirror.com/fsevents@2.3.2 + dev: true + + registry.npmmirror.com/source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz} + name: source-map-js + version: 1.0.2 + engines: {node: '>=0.10.0'} + dev: true + + registry.npmmirror.com/typescript@5.0.2: + resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/typescript/-/typescript-5.0.2.tgz} + name: typescript + version: 5.0.2 + engines: {node: '>=12.20'} + hasBin: true + dev: true + + registry.npmmirror.com/vite@4.3.2: + resolution: {integrity: sha512-9R53Mf+TBoXCYejcL+qFbZde+eZveQLDYd9XgULILLC1a5ZwPaqgmdVpL8/uvw2BM/1TzetWjglwm+3RO+xTyw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vite/-/vite-4.3.2.tgz} + name: vite + version: 4.3.2 + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: registry.npmmirror.com/esbuild@0.17.19 + postcss: registry.npmmirror.com/postcss@8.4.23 + rollup: registry.npmmirror.com/rollup@3.22.0 + optionalDependencies: + fsevents: registry.npmmirror.com/fsevents@2.3.2 + dev: true + + registry.npmmirror.com/yaml@2.3.0-5: + resolution: {integrity: sha512-Y2NEcp/A4OjsMxDenJrPqucy/ufkbfQu7RRjKYWLfyATH+oGJKkPWp/4mnHPCnBpIUH29P/F8uemt/g0xB3VQg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/yaml/-/yaml-2.3.0-5.tgz} + name: yaml + version: 2.3.0-5 + engines: {node: '>= 14', npm: '>= 7'} + dev: false diff --git a/src/main/ts/src/graph.ts b/src/main/ts/src/graph.ts new file mode 100644 index 000000000..c64bb39a5 --- /dev/null +++ b/src/main/ts/src/graph.ts @@ -0,0 +1,152 @@ +import { parse } from "yaml"; + +export class Graph{ + metadata: Metadata; + relation: Relation; + graph: Map; + sourceNodes: number[]; + sinkNodes: number[]; + recommendedPaths: number[][]; + + constructor(data: any){ + this.metadata = new Metadata(data.metadata); + this.relation = new Relation(data.relation, this.metadata); + this.graph = new Map(); + this.initGraph(data.graph); + this.sourceNodes = data.sourceNodes; + this.sinkNodes = data.sinkNodes; + + this.recommendedPaths = (data.recommendedPaths as number[][]).map(path=>path.filter(n => !this.isField(n))); + // console.log(this.recommandedPaths); + // console.log(this.sourceNodes + " " + this.sinkNodes) + // console.log(this.metadata.getvf(6)) + // console.log(this.metadata.getvf(25)) + // console.log(this.metadata.getvf(26)) + // console.log(this.metadata.getvf(27)) + } + + initGraph(graph: any){ + Object.entries(graph).forEach(entry=>{ + const toNodess: number[] = []; + (entry[1] as number[]).forEach(n=>{ + if(this.isField(n)){ + toNodess.push(...(graph[n] as number[])); + }else{ + toNodess.push(n); + } + }); + const toNodes = Array.from(new Set(toNodess)); + this.graph.set(parseInt(entry[0]), toNodes); + }); + } + + isField(num:number){ + return this.relation.fields.includes(num); + } + + isSource(num:number){ + return this.sourceNodes.includes(num); + } + + isSink(num:number){ + return this.sinkNodes.includes(num); + } +} + +class Metadata{ + packages: string[]; + classes: string[]; + methods: string[]; + varsAndFields: string[]; + + constructor(metadata: any){ + this.packages = metadata.packages; + this.classes = metadata.classes; + this.methods = metadata.methods; + this.varsAndFields = metadata.varsAndFields; + + const len: number = this.classes.length; + for(let i = 0; i < len; i++){ + if(!this.classes[i].includes('.')){ + // set the class with no parent package different to its package + this.classes[i] = '.' + this.classes[i]; + } + } + } + + getp(index: number) { + return this.packages[index]; + } + + getc(index: number) { + return this.classes[index]; + } + + getm(index: number) { + return this.methods[index]; + } + + getvf(index: number) { + return this.varsAndFields[index]; + } +} + +class Relation{ + packageToClasses: Map; + classToMethods: Map; + methodToVars: Map; + + fields: number[]; + // jdkVars: number[]; + + constructor(relation: any, metadata: Metadata){ + this.packageToClasses = new Map(); + this.classToMethods = new Map(); + this.methodToVars = new Map(); + this.fields = Object.values(relation.classToFields).flat() as number[]; + + // const jdkPackages: number[] = []; + Object.entries(relation.packageToClasses).forEach(entry=>{ + this.packageToClasses.set(parseInt(entry[0]), entry[1] as number[]); + // if(metadata.getp(parseInt(entry[0])) === "java.lang"){ // todo: + // jdkPackages.push(parseInt(entry[0])); + // } + }); + + Object.entries(relation.classToMethods).forEach(entry=>{ + this.classToMethods.set(parseInt(entry[0]), entry[1] as number[]); + }); + + Object.entries(relation.methodToVars).forEach(entry=>{ + this.methodToVars.set(parseInt(entry[0]), entry[1] as number[]); + }); + + // const jdkClasses: number[] = (Array.from(jdkPackages, p => this.packageToClasses.get(p)).flat().filter(item => item !== undefined) as number[]); + // const jdkMethods: number[] = (Array.from(jdkClasses, p => this.classToMethods.get(p)).flat().filter(item => item !== undefined) as number[]); + // this.jdkVars = (Array.from(jdkMethods, p => this.methodToVars.get(p)).flat().filter(item => item !== undefined) as number[]); + } + +} + +export function build(file:File, visualize:(graph:Graph)=>void){ + const reader = new FileReader(); + + reader.onload = function(e){ + const yamlContent = e.target?.result; + if(typeof yamlContent != 'string'){ + console.log('yamlContent不是字符串类型'); + return; + } + // try{ + const parsedData = parse((yamlContent as string)); + const graph:Graph = new Graph(parsedData); + console.log('finish build, begin visualization'); + visualize(graph); + // } + // catch{ + // console.log(e); + // } + } + + reader.readAsText(file); +} \ No newline at end of file diff --git a/src/main/ts/src/handle-event.ts b/src/main/ts/src/handle-event.ts new file mode 100644 index 000000000..92c8ac949 --- /dev/null +++ b/src/main/ts/src/handle-event.ts @@ -0,0 +1,33 @@ +export function eventHandler(elem: HTMLElement, fileHandler: (file: File)=>void){ + elem.addEventListener('dragenter', function(event) { + event.preventDefault(); + elem.style.background = '#f7f7f7'; + elem.textContent = "松开文件进行处理"; + }); + elem.addEventListener('dragleave', function(event) { + event.preventDefault(); + elem.style.background = '#ffffff'; + elem.textContent = "拖拽文件进入此处"; + }); + elem.addEventListener('dragover', function(event) { + event.preventDefault(); + }); + elem.addEventListener('drop', function(event){ + event.preventDefault(); + elem.style.background = '#ffffff'; + + let file = (event as DragEvent).dataTransfer?.files[0]; + + if(file === undefined){ + elem.textContent = "未接收到文件"; + return; + } + if(!file.name.endsWith('.yml')){ + elem.textContent = "接收到非yaml文件"; + return; + } + elem.textContent = "拖拽文件进入此处"; + console.log('handle event over, begin building graph'); + fileHandler(file); + }); +} \ No newline at end of file diff --git a/src/main/ts/src/main.ts b/src/main/ts/src/main.ts new file mode 100644 index 000000000..200c75691 --- /dev/null +++ b/src/main/ts/src/main.ts @@ -0,0 +1,10 @@ +import './style.css' +import { build } from './graph' +import { eventHandler } from './handle-event' +import { visualize } from './visualization'; + +const inputElem: HTMLElement = document.getElementById('input') as HTMLElement; +eventHandler(inputElem, (file: File)=>{ + build(file, visualize); +}); + diff --git a/src/main/ts/src/style.css b/src/main/ts/src/style.css new file mode 100644 index 000000000..05039e094 --- /dev/null +++ b/src/main/ts/src/style.css @@ -0,0 +1,106 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #3178c6aa); +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} + +.button-container { + position: absolute; /* 设置容器为绝对定位 */ + top: 40px; /* 距离页面顶部 40px */ + left: 20px; /* 距离页面左侧 20px */ + display: flex; /* 使用 flexbox 布局 */ + flex-direction: column; /* 设置为垂直排列 */ +} + +button { + border-radius: 8px; + border: 1px solid #1a1a1a; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; + margin-bottom: 10px; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/src/main/ts/src/visualization.ts b/src/main/ts/src/visualization.ts new file mode 100644 index 000000000..18c10c3d7 --- /dev/null +++ b/src/main/ts/src/visualization.ts @@ -0,0 +1,551 @@ +import go, { Group, Model } from 'gojs'; +import { Graph } from './graph' ; +import { Alias } from 'yaml'; + +// const $ = go.GraphObject.make; +// const myDiagram = $(go.Diagram, "myDiagramDiv", +// { +// // layout: $(go.GridLayout, +// // { +// // wrappingColumn: 6, +// // arrangement: go.GridLayout.Ascending, +// // spacing: new go.Size(50,50), +// // isRealtime: false, +// // }) +// layout: $(go.TreeLayout, +// {angle: 90} +// ), +// "undoManager.isEnabled": true +// }); + +const $ = go.GraphObject.make; + +const myDiagram = +new go.Diagram("myDiagramDiv", + { + padding: 10, + layout: $(go.LayeredDigraphLayout, + { + direction: 90, + layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource, + alignOption: go.LayeredDigraphLayout.AlignAll + }), + "undoManager.isEnabled": true + }); + +export function visualize(graph: Graph){ + const nodeDataArray = makeNodes(graph); + const linkDataArray = makeLinks(graph); + + myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray); + + myDiagram.nodeTemplate = + $(go.Node, "Auto", + new go.Binding("visible"), + $(go.Shape, "Rectangle", {fill: "lightblue"}, + new go.Binding("fill", "color"), + new go.Binding("stroke", "isHighlighted", h => h ? "red" : "black").ofObject(), + new go.Binding("strokeWidth", "isHighlighted", h => h ? 4 : 2).ofObject(), + ), + $(go.TextBlock, "isla", { margin: 10 }, + new go.Binding("text", "key") + ), + { + selectionAdornmentTemplate: + $(go.Adornment, "Spot", + $(go.Panel, "Auto", + $(go.Shape, { fill: null, stroke: "black", strokeWidth: 4 }), + $(go.Placeholder), + ), + $("Button", + $(go.TextBlock, "trace"), + { alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Center, desiredSize: new go.Size(70,25), click: highlightPath }, + ) + ), + }, + $("Button", // a replacement for "TreeExpanderButton" that works for non-tree-structured graphs + // assume initially not visible because there are no links coming out + { visible: false }, + // bind the button visibility to whether it's not a leaf node + new go.Binding("visible", "isTreeLeaf", leaf => !leaf).ofObject(), + $(go.Shape, + { + name: "ButtonIcon", + figure: "MinusLine", + desiredSize: new go.Size(6, 6) + }, + new go.Binding("figure", "isCollapsed", // data.isCollapsed remembers "collapsed" or "expanded" + collapsed => collapsed ? "PlusLine" : "MinusLine")), + { + click: (e, obj) => { + e.diagram.startTransaction(); + var node = obj.part as go.Node; + if (node.data.isCollapsed) { + expandFrom(node, node); + } else { + collapseFrom(node, node); + } + e.diagram.commitTransaction("toggled visibility of dependencies"); + } + } + ) + ); + + myDiagram.linkTemplate = + $(go.Link, + { curve: go.Link.Bezier }, + $(go.Shape, { strokeWidth: 3 }, + new go.Binding("stroke", "isHighlighted", h => h ? "red" : "black").ofObject(), + new go.Binding("strokeWidth", "isHighlighted", h => h ? 4 : 2).ofObject(), + ), + $(go.Shape, { toArrow: "Standard", strokeWidth: 3 }, + new go.Binding("stroke", "isHighlighted", h => h ? "red" : "black").ofObject(), + ), + ); + + myDiagram.groupTemplate = + $(go.Group, "Auto", + { + // layout: $(go.GridLayout, + // { wrappingColumn: 2, arrangement: go.GridLayout.Ascending, spacing: new go.Size(50,50), isRealtime: false }), + // isSubGraphExpanded: false, + layout: $(go.LayeredDigraphLayout, + { + direction: 90, + layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource, + alignOption: go.LayeredDigraphLayout.AlignAll + }), + isSubGraphExpanded: false + }, + $(go.Shape, "RoundedRectangle", + { fill: null, stroke: "gray", strokeWidth: 2 }), + $(go.Panel, "Vertical", + { defaultAlignment: go.Spot.Left, margin: 4 }, + $(go.Panel, "Horizontal", + { defaultAlignment: go.Spot.Top }, + $("SubGraphExpanderButton"), + $(go.TextBlock, + { font: "Bold 18px Sans-Serif", margin: 4 }, + new go.Binding("text", "key")), + ), + $(go.Placeholder, + { padding: new go.Margin(0, 10) }), + ), + ); + myDiagram.nodes.each(function(n) { + // myDiagram.startTransaction(); + n.visible = false; + // myDiagram.commitTransaction("hide all nodes"); + }) + myDiagram.nodes.each(function(n) { + if(n instanceof go.Node && !(n instanceof go.Group) && !n.findLinksInto().next()){ + var nd = n as go.Node; + nd.visible = true; + while(nd.containingGroup !== null){ + (nd.containingGroup as go.Group).visible = true; + nd = nd.containingGroup; + } + } + }) + myDiagram.links.each(link => link.visible = false); + + if(!buttonCreated){ + LayersChangeButton("Remove Package", "Restore Package", graph, restorePackages, removePackages); + LayersChangeButton("Remove Classes", "Restore Class", graph, restoreClasses, removeClasses); + LayersChangeButton("Remove Methods", "Restore Method", graph, restoreMethods, removeMethods); + + HighlightPathButton("Highlight Recommanded Paths", graph, highlightRecommandedPath); + + ResetButton(graph, hideAll); + buttonCreated = true; + } + + // myDiagram.layout = $(go.TreeLayout, { angle: 90 }); + + // myDiagram.model = new go.GraphLinksModel( + // [ { key: 1 }, + // { key: 2 }, + // { key: 3 }, + // ], + // [ { from: 1, to: 3 }, + // { from: 2, to: 3 }] ); + // myDiagram.nodes.each(function(n) { + // n.wasTreeExpanded = false; + // n.isTreeExpanded = false; + // }) +} + +// ===================== LayersChangeButton Function ========================= + +var buttonCreated = false; +// 记录各层次是否还在图中出现,如packageExist = false代表图中不出现Package层次 +let packageExist = true; +let classExist = true; +let methodExist = true; +function LayersChangeButton(restoreState : string, removeState : string, graph : Graph, restoreFunc : (graph : Graph) => void, removeFunc : (graph : Graph) => void){ + var button = document.createElement("button"); + button.innerHTML = restoreState; + document.getElementById("buttonContainer")?.appendChild(button); + button.addEventListener("click", function(){ + if(button.innerHTML === restoreState){ + button.innerHTML = removeState; + removeFunc(graph); + } + else if(button.innerHTML === removeState){ + button.innerHTML = restoreState; + restoreFunc(graph); + } + }) +} + +function removePackages(graph : Graph){ + myDiagram.startTransaction("remove Package"); + graph.metadata.packages.forEach(removeGroup); + myDiagram.commitTransaction("remove Package"); + packageExist = false; +} + +function removeClasses(graph : Graph){ + myDiagram.startTransaction("remove Class"); + graph.metadata.classes.forEach(removeGroup); + myDiagram.commitTransaction("remove Class"); + classExist = false; +} + +function removeMethods(graph : Graph){ + myDiagram.startTransaction("remove Method"); + graph.metadata.methods.forEach(removeGroup); + myDiagram.commitTransaction("remove Method"); + methodExist = false; +} + +function removeGroup(name: string){ + var group = myDiagram.findNodeForKey(name) as go.Group; + if(group === null) return; + + (group.diagram as go.Diagram).model.setDataProperty(group, "isSubGraphExpanded", true); + group.memberParts.each(nodeOrGroup => (nodeOrGroup.diagram as go.Diagram).model.setDataProperty(nodeOrGroup.data, "group", group.containingGroup? group.containingGroup.key : null)); + myDiagram.model.removeNodeData(myDiagram.model.findNodeDataForKey(name) as go.ObjectData); +} + + +function restorePackages(graph : Graph){ + myDiagram.startTransaction("restore Package"); + // 恢复Package层需要先恢复Class层,而后再将Class层删去,做到只恢复Package层的效果 + if(!classExist){ + restoreClasses(graph); + graph.relation.packageToClasses.forEach((vc,kp) => restoreGroup(graph.metadata.getp(kp), vc.filter(v => v !== undefined).map(v => graph.metadata.getc(v)))); + removeClasses(graph); + }else{ + graph.relation.packageToClasses.forEach((vc,kp) => restoreGroup(graph.metadata.getp(kp), vc.filter(v => v !== undefined).map(v => graph.metadata.getc(v)))); + } + myDiagram.commitTransaction("restore Package"); + packageExist = true; +} + +function restoreClasses(graph : Graph){ + myDiagram.startTransaction("restore Class"); + // 恢复Class层需要先恢复Method层,理由同上 + if(!methodExist){ + restoreMethods(graph); + graph.relation.classToMethods.forEach((vm,kc) => restoreGroup(graph.metadata.getc(kc), vm.filter(v => v !== undefined).map(v => graph.metadata.getm(v)))); + removeMethods(graph); + }else{ + graph.relation.classToMethods.forEach((vm,kc) => restoreGroup(graph.metadata.getc(kc), vm.filter(v => v !== undefined).map(v => graph.metadata.getm(v)))); + } + myDiagram.commitTransaction("restore Class"); + classExist = true; +} + +function restoreMethods(graph : Graph){ + myDiagram.startTransaction("restore Method"); + graph.relation.methodToVars.forEach((vv,km) => + restoreGroup(graph.metadata.getm(km), vv.filter(v => v !== undefined).map(v => graph.metadata.getvf(v)))); + myDiagram.commitTransaction("restore Method"); + methodExist = true; +} + +function restoreGroup(name: string, members : string[]){ + myDiagram.model.addNodeData({key: name, isGroup: true, isHighlighted: false, isCollapsed: false}); + + const parent = myDiagram.findNodeForKey(name) as go.Group; // 要恢复的Group + let containGroupKey; + + // 将要恢复的Group展开 + (parent.diagram as go.Diagram).model.setDataProperty(parent, "isSubGraphExpanded", true); + members.forEach(n=>{ + var node = myDiagram.findNodeForKey(n) as go.Node; + if(node === null){ + console.error("No such node found in Diagram"); + return; + } + containGroupKey = node.containingGroup?.key; + (node.diagram as go.Diagram).model.setDataProperty(node.data, "group", name); + }); + + // 如果发现恢复的Group内部没有任何成员可见,那么折叠这个Group并使其不可见 + if(parent.memberParts.all(m => !m.isVisible())){ + (parent.diagram as go.Diagram).model.setDataProperty(parent, "isSubGraphExpanded", false); + (parent.diagram as go.Diagram).model.setDataProperty(parent, "visible", false); + } + + // 设置恢复Group的containingGroup + (parent.diagram as go.Diagram).model.setDataProperty(parent.data, "group", containGroupKey); +} + +// ====================== HighlightPathButton ==================== + +// todo: 找到对所有路径的合适可视化方式 +function HighlightPathButton(text: string, graph: Graph, highlight: (graph : Graph) => void){ + var button = document.createElement("button"); + button.innerHTML = text; + document.getElementById("buttonContainer")?.appendChild(button); + button.addEventListener("click", function(){ + highlight(graph); + }) +} + +function highlightRecommandedPath(graph: Graph){ + myDiagram.startTransaction("highlight recommanded paths"); + graph.recommendedPaths.forEach(path=>{ + // path.forEach(n=>{ + // let node = myDiagram.findNodeForKey(graph.metadata.getvf(n)); + // if(node !== null) { + // node.diagram?.model.setDataProperty(node.data, "isHighlighted", true); + // } + // }) + for(var n = 0; n < path.length - 1; n++){ + const currNode = myDiagram.findNodeForKey(graph.metadata.getvf(path[n])); + const nextNode = myDiagram.findNodeForKey(graph.metadata.getvf(path[n + 1])); + if(currNode === null || nextNode === null){ + console.error("Nodes not found, isField-n: " + graph.isField(path[n]) + ", isField-(n+1)" + graph.isField(path[n + 1])); + return; + } + + currNode.diagram?.model.setDataProperty(currNode, "isHighlighted", true); + currNode.diagram?.model.setDataProperty(currNode.data, "visible", true); + currNode.diagram?.model.setDataProperty(currNode.data, "isCollapsed", false); + var cg = currNode.containingGroup; + while(cg !== null && cg.visible !== true){ + (cg.diagram as go.Diagram).model.setDataProperty(cg, "visible", true); + cg = cg.containingGroup; + } + + + currNode.findLinksTo(nextNode).each(l => { + l.diagram?.model.setDataProperty(l, "isHighlighted", true); + l.diagram?.model.setDataProperty(l, "visible", true); + }); + } + + const lastNode = myDiagram.findNodeForKey(graph.metadata.getvf(path[path.length - 1])); + if(lastNode === null){ + console.error("Nodes not found, isField-n: " + graph.isField(path[path.length - 1])); + return; + } + lastNode.diagram?.model.setDataProperty(lastNode, "isHighlighted", true); + lastNode.diagram?.model.setDataProperty(lastNode.data, "visible", true); + lastNode.diagram?.model.setDataProperty(lastNode.data, "isCollapsed", false); + var cg = lastNode.containingGroup; + while(cg !== null && cg.visible !== true){ + (cg.diagram as go.Diagram).model.setDataProperty(cg, "visible", true); + cg = cg.containingGroup; + } + }) + myDiagram.commitTransaction("highlight recommanded paths"); +} + +// ===================== ResetButton ======================== + +function ResetButton(graph: Graph, hide: (graph: Graph) => void){ + var button = document.createElement("button"); + button.innerHTML = "Reset"; + document.getElementById("buttonContainer")?.appendChild(button); + button.addEventListener("click", function(){ + hide(graph); + }) +} + +function hideAll(graph: Graph){ + myDiagram.startTransaction("hide All"); + myDiagram.nodes.each(n=>{ + if(n instanceof go.Node) { + n.diagram?.model.setDataProperty(n.data, "isCollapsed", true); + n.diagram?.model.setDataProperty(n.data, "visible", false); + while(n.containingGroup !== null){ + let nd = n.containingGroup as go.Group; + if(nd.memberParts.any(m => m.isVisible())) break; + nd.diagram?.model.setDataProperty(nd, "isSubGraphExpanded", false); + nd.diagram?.model.setDataProperty(nd, "visible", false); + n = nd; + } + } + }); + myDiagram.links.each(l=>l.diagram?.model.setDataProperty(l, "visible", false)); + graph.sourceNodes.forEach(num => { + const s = graph.metadata.getvf(num); + var n = myDiagram.findNodeForKey(s) as go.Node; + n.diagram?.model.setDataProperty(n, "visible", true); + while(n.containingGroup !== null){ + let nd = n.containingGroup as go.Group; + nd.diagram?.model.setDataProperty(nd, "isSubGraphExpanded", false); + nd.diagram?.model.setDataProperty(nd, "visible", true); + n = nd; + } + }) + myDiagram.commitTransaction("hide All"); + // visualize(graph); +} + +// =============================================================== +// =============================================================== + +myDiagram.click = e => { + e.diagram.commit(d => d.clearHighlighteds(), "clear highlights"); +} + +// ====================== Fold & Expand Event ==================== +function collapseFrom(node: go.Node, start: go.Node) { + // 若该节点还有可见的入边,则保留 + if (node.findLinksInto().any(link => link.visible) && node !== start){ + return; + } + + if (node.data.isCollapsed) { + collapse(node); + return; + } + + (node.diagram as go.Diagram).model.setDataProperty(node.data, "isCollapsed", true); + + if (node !== start) { + collapse(node); + } + node.findLinksOutOf().each(link => (link.diagram as go.Diagram).model.setDataProperty(link, "visible", false)) + node.findNodesOutOf().each(n => collapseFrom(n, start)); + + function collapse(node: go.Node){ + (node.diagram as go.Diagram).model.setDataProperty(node.data, "visible", false); + + // 若Group内没有可见的Node,则Group也隐藏 + var cg = node.containingGroup; + while(cg !== null){ + if (cg.memberParts.any(n => n.visible)) { + break; + } + + (cg.diagram as go.Diagram).model.setDataProperty(cg, "visible", false); + cg = cg.containingGroup; + } + } +} + +function expandFrom(node: go.Node, start: go.Node) { + if (!node.data.isCollapsed) + return; + + (node.diagram as go.Diagram).model.setDataProperty(node.data, "isCollapsed", false); + + node.findNodesOutOf().each(n => { + (n.diagram as go.Diagram).model.setDataProperty(n.data, "visible", true); + + // 若有Node变为可见,则Node所在的Group也得可见 + var cg = n.containingGroup; + while(cg !== null && cg.visible !== true){ + (cg.diagram as go.Diagram).model.setDataProperty(cg, "visible", true); + cg = cg.containingGroup; + } + } + ) + node.findLinksOutOf().each(link => (link.diagram as go.Diagram).model.setDataProperty(link, "visible", true)) +} +// =============================================================== + + +function highlightPath(event: go.InputEvent, ador: go.GraphObject){ + const diagram = ador.diagram as go.Diagram; + const node = ((ador.part as go.Adornment).adornedPart) as go.Node; + diagram.startTransaction("highlight"); + diagram.clearHighlighteds(); + diagram.findLayer("Foreground")?.parts.each(p=>{ + if(p instanceof go.Link){ + p.layerName = ""; + } + }) + mark(node, 4); + diagram.commitTransaction("highlight"); +} +function mark(node: go.Node, depth: number){ + if(depth === 0) return; + + node.isHighlighted = true; + node.findLinksOutOf().each(l =>{ + l.isHighlighted = true; + l.layerName = "Foreground"; + }); + node.findNodesOutOf().each(n => mark(n, depth - 1)); +} + + + +function makeNodes(graph: Graph){ + const nodeDataArray: INode[] = []; + + graph.relation.packageToClasses.forEach((vc,kp)=>{ + const pName:string = graph.metadata.getp(kp) + + nodeDataArray.push({key: pName, isGroup: true, isHighlighted: false, isCollapsed: true}); + vc.forEach(n=>{ + if(graph.relation.classToMethods.get(n) !== undefined){ + nodeDataArray.push({key:graph.metadata.getc(n), isGroup: true, group: pName, isHighlighted: false, isCollapsed: true}); + } + }); + }) + + graph.relation.classToMethods.forEach((vm, kc)=>{ + const cName:string = graph.metadata.getc(kc); + vm.forEach(n=>{ + if(graph.relation.methodToVars.get(n) !== undefined){ + nodeDataArray.push({key:graph.metadata.getm(n), isGroup: true, group: cName, isHighlighted: false, isCollapsed: true}); + } + }); + }) + + graph.relation.methodToVars.forEach((vv, km)=>{ + const mName:string = graph.metadata.getm(km); + vv.forEach(n=>{ + const color:string = graph.isSource(n)? 'gold' : (graph.isSink(n)? 'aquamarine' : 'lightblue'); + nodeDataArray.push({key: graph.metadata.getvf(n), color: color, group: mName, isHighlighted: false, isCollapsed: true}); + }); + }) + console.log('finish nodes making'); + return nodeDataArray; +} + +function makeLinks(graph: Graph){ + const linkDataArray: ILink[] = []; + + graph.graph.forEach((v,k)=>{ + v.forEach(n=>linkDataArray.push({from: graph.metadata.getvf(k), to: graph.metadata.getvf(n), color: 'black', isHighlighted: false, zOrder: 0})); + }) + console.log('finish links making'); + return linkDataArray; +} + +interface INode{ + key?: string; + color?: string; + isGroup?: boolean; + group?: string; + isHighlighted: boolean; + isCollapsed: boolean; +} + +interface ILink{ + key?: string; + from: string; + to: string; + color?: string; + isHighlighted: boolean; + zOrder?: number; +} \ No newline at end of file diff --git a/src/main/ts/src/vite-env.d.ts b/src/main/ts/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/src/main/ts/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/main/ts/tsconfig.json b/src/main/ts/tsconfig.json new file mode 100644 index 000000000..5e17e93b5 --- /dev/null +++ b/src/main/ts/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +}