New business card page
This commit is contained in:
Generated
+306
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@ztimson/momentum": "^1.5.4",
|
||||
"@ztimson/utils": "^0.30.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"vue": "^3.3.11",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
@@ -1369,6 +1370,15 @@
|
||||
"var-persist": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
@@ -1419,6 +1429,15 @@
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||
@@ -1435,6 +1454,35 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/computeds": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz",
|
||||
@@ -1486,6 +1534,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
@@ -1497,6 +1554,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dijkstrajs": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||
@@ -1554,6 +1623,19 @@
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -1569,6 +1651,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
@@ -1597,6 +1688,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
@@ -1628,6 +1728,18 @@
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-locate": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -1732,6 +1844,42 @@
|
||||
"npm": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-try": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-limit": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
@@ -1739,6 +1887,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
@@ -1782,6 +1939,15 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pngjs": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.13",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz",
|
||||
@@ -1810,6 +1976,23 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
|
||||
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dijkstrajs": "^1.0.1",
|
||||
"pngjs": "^5.0.0",
|
||||
"yargs": "^15.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"qrcode": "bin/qrcode"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-json-fast": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",
|
||||
@@ -1838,6 +2021,21 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.60.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz",
|
||||
@@ -1917,6 +2115,12 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -1962,6 +2166,32 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
@@ -2129,6 +2359,82 @@
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which-module": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"dependencies": {
|
||||
"@ztimson/momentum": "^1.5.4",
|
||||
"@ztimson/utils": "^0.30.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"vue": "^3.3.11",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
|
||||
+1
-24
@@ -1,31 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import Foot from '@/components/foot.vue';
|
||||
import Profile from '@/components/profile.vue';
|
||||
import {momentum} from '@/services/momentum.service';
|
||||
|
||||
momentum.client.inject();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Spacer -->
|
||||
<div class="w-100" style="height: min(75vh, 500px)"></div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="cap-width mb-3 bg-white">
|
||||
<!-- Header -->
|
||||
<header class="px-4 d-flex justify-content-center justify-content-sm-start" style="background: #732222">
|
||||
<profile style="transform: translateY(-33%)" />
|
||||
</header>
|
||||
|
||||
<!-- Body -->
|
||||
<main class="p-3">
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<foot />
|
||||
</div>
|
||||
|
||||
<!-- Spacer -->
|
||||
<div class="d-none d-lg-block w-100" style="height: 40px"></div>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
@@ -17,7 +17,7 @@ import Icon from '@/components/icon.vue';
|
||||
<div class="me-sm-5">
|
||||
<h1 class="m-0" style="font-size: 2.5rem">Zakary Timson</h1>
|
||||
<h2 class="mt-0 text-muted" style="font-size: 1.25rem">
|
||||
AI SOFTWARE ENGINEER
|
||||
SOFTWARE ENGINEER
|
||||
</h2>
|
||||
<ul class="m-0 p-0 text-start" style="list-style: none">
|
||||
<li><icon name="map-marker-alt" class="me-1"/> London Ontario, Canada</li>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import './main.scss'
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import router from './routes'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import BusinessCard from '@/views/BusinessCard.vue';
|
||||
import Layout from '@/views/Layout.vue';
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '@/views/Home.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory((<any>import.meta).env.BASE_URL),
|
||||
routes: [
|
||||
{path: '/', name: 'home', component: Home}
|
||||
{path: '/', component: Layout, children: [{path: '', component: Home}]},
|
||||
{path: '/card', name: 'home', component: BusinessCard}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -0,0 +1,432 @@
|
||||
<script setup lang="ts">
|
||||
import Icon from '@/components/icon.vue';
|
||||
import QRCode from 'qrcode';
|
||||
import {computed, onMounted, ref} from 'vue';
|
||||
|
||||
// ---------- Your info (edit here) ----------
|
||||
const person = {
|
||||
first: 'Zakary',
|
||||
last: 'Timson',
|
||||
title: 'Software Engineer',
|
||||
org: 'ZaksCode',
|
||||
email: 'zaktimson@gmail.com',
|
||||
phone: '+1 (519) 917-1046', // e.g. '+1 519 555 0123' - leave empty to hide
|
||||
city: 'London',
|
||||
region: 'Ontario',
|
||||
country: 'Canada',
|
||||
website: 'https://zakscode.com',
|
||||
github: 'https://github.com/ztimson',
|
||||
git: 'https://git.zakscode.com',
|
||||
photo: '/profile.jpg'
|
||||
};
|
||||
|
||||
// ---------- Rows displayed on the card ----------
|
||||
const rows = computed(() => [
|
||||
{key: 'email', label: 'Email', value: person.email, href: `mailto:${person.email}`, icon: 'envelope'},
|
||||
person.phone && {key: 'phone', label: 'Phone', value: person.phone, href: `tel:${person.phone.replace(/[^+\d]/g, '')}`, icon: 'phone'},
|
||||
{key: 'website', label: 'Website', value: person.website.replace(/^https?:\/\//, ''), copy: person.website, href: person.website, icon: 'globe'},
|
||||
].filter(Boolean) as any[]);
|
||||
|
||||
// ---------- Copy to clipboard ----------
|
||||
const copied = ref('');
|
||||
let copyTimer: any;
|
||||
|
||||
async function copy(key: string, text: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
} catch {
|
||||
const el = document.createElement('textarea');
|
||||
el.value = text;
|
||||
el.style.position = 'fixed';
|
||||
el.style.opacity = '0';
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
el.remove();
|
||||
}
|
||||
copied.value = key;
|
||||
clearTimeout(copyTimer);
|
||||
copyTimer = setTimeout(() => copied.value = '', 1600);
|
||||
}
|
||||
|
||||
// ---------- vCard ----------
|
||||
const esc = (s: string) => s.replace(/\\/g, '\\\\').replace(/;/g, '\\;').replace(/,/g, '\\,').replace(/\r?\n/g, '\\n');
|
||||
const fold = (line: string) => line.match(/.{1,74}/g)?.join('\r\n ') ?? line;
|
||||
|
||||
function buildVCard(photoBase64?: string) {
|
||||
const lines = [
|
||||
'BEGIN:VCARD',
|
||||
'VERSION:3.0',
|
||||
`N:${esc(person.last)};${esc(person.first)};;;`,
|
||||
`FN:${esc(person.first + ' ' + person.last)}`,
|
||||
`ORG:${esc(person.org)}`,
|
||||
`TITLE:${esc(person.title)}`,
|
||||
`EMAIL;TYPE=INTERNET,WORK:${person.email}`,
|
||||
person.phone && `TEL;TYPE=CELL:${person.phone}`,
|
||||
`ADR;TYPE=WORK:;;;${esc(person.city)};${esc(person.region)};;${esc(person.country)}`,
|
||||
`URL:${person.website}`,
|
||||
`item1.URL:${person.github}`,
|
||||
'item1.X-ABLabel:GitHub',
|
||||
`item2.URL:${person.git}`,
|
||||
'item2.X-ABLabel:Git',
|
||||
photoBase64 && `PHOTO;ENCODING=b;TYPE=JPEG:${photoBase64}`,
|
||||
'END:VCARD'
|
||||
].filter(Boolean) as string[];
|
||||
return lines.map(fold).join('\r\n') + '\r\n';
|
||||
}
|
||||
|
||||
// Shrink the profile picture so the .vcf stays small
|
||||
async function loadPhoto(size = 256): Promise<string | undefined> {
|
||||
try {
|
||||
const blob = await (await fetch(person.photo)).blob();
|
||||
const bmp = await createImageBitmap(blob);
|
||||
const side = Math.min(bmp.width, bmp.height);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = canvas.height = size;
|
||||
canvas.getContext('2d')!.drawImage(
|
||||
bmp, (bmp.width - side) / 2, (bmp.height - side) / 2, side, side, 0, 0, size, size
|
||||
);
|
||||
return canvas.toDataURL('image/jpeg', 0.8).split(',')[1];
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const saving = ref(false);
|
||||
|
||||
async function downloadContact() {
|
||||
saving.value = true;
|
||||
try {
|
||||
const vcf = buildVCard(await loadPhoto());
|
||||
const url = URL.createObjectURL(new Blob([vcf], {type: 'text/vcard;charset=utf-8'}));
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${person.first}-${person.last}.vcf`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- QR code (no photo, so it stays easy to scan) ----------
|
||||
const qr = ref('');
|
||||
onMounted(async () => {
|
||||
qr.value = await QRCode.toDataURL(buildVCard(), {
|
||||
errorCorrectionLevel: 'M',
|
||||
margin: 1,
|
||||
width: 600,
|
||||
color: {dark: '#1c1c1e', light: '#ffffff'}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bc-page">
|
||||
<article class="bc">
|
||||
<!-- Banner -->
|
||||
<div class="bc-banner">
|
||||
<span class="bc-ring bc-ring--1"></span>
|
||||
<span class="bc-ring bc-ring--2"></span>
|
||||
<span class="bc-ring bc-ring--3"></span>
|
||||
</div>
|
||||
|
||||
<!-- Identity -->
|
||||
<div class="bc-identity">
|
||||
<img class="bc-avatar" :src="person.photo" :alt="`${person.first} ${person.last}`">
|
||||
<h1 class="bc-name">{{person.first}} {{person.last}}</h1>
|
||||
<div class="bc-title">{{person.title}}</div>
|
||||
</div>
|
||||
|
||||
<!-- Details -->
|
||||
<ul class="bc-list">
|
||||
<li v-for="r in rows" :key="r.key" class="bc-row">
|
||||
<span class="bc-row-icon"><icon :name="r.icon" :class="{'fa-brands': r.brand}" /></span>
|
||||
<div class="bc-row-text">
|
||||
<span class="bc-row-label">{{r.label}}</span>
|
||||
<a v-if="r.href" class="bc-row-value" :href="r.href" :target="r.href.startsWith('http') ? '_blank' : undefined" rel="noopener">{{r.value}}</a>
|
||||
<span v-else class="bc-row-value">{{r.value}}</span>
|
||||
</div>
|
||||
<button type="button" class="bc-copy" :class="{'bc-copy--done': copied == r.key}" :aria-label="`Copy ${r.label}`" @click="copy(r.key, r.copy || r.value)">
|
||||
<icon :name="copied == r.key ? 'check' : 'copy'" />
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- QR -->
|
||||
<div class="bc-qr">
|
||||
<div class="bc-qr-box">
|
||||
<img v-if="qr" :src="qr" alt="Contact QR code">
|
||||
</div>
|
||||
<div class="bc-qr-caption">Scan to add me to your contacts</div>
|
||||
<a v-if="qr" class="bc-qr-link" :href="qr" :download="`${person.first}-${person.last}-QR.png`">Download QR</a>
|
||||
</div>
|
||||
|
||||
<!-- Primary action -->
|
||||
<div class="bc-section">
|
||||
<button type="button" class="bc-save" :disabled="saving" @click="downloadContact">
|
||||
<icon name="user-plus" /> Save to Contacts
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Toast -->
|
||||
<div class="bc-toast" :class="{'bc-toast--show': copied}" role="status">Copied to clipboard</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.bc-page {
|
||||
--bc-accent: #732222;
|
||||
--bc-accent-2: #a63a3a;
|
||||
--bc-ink: #1c1c1e;
|
||||
--bc-muted: #7a7a82;
|
||||
--bc-line: #ececf0;
|
||||
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px 16px;
|
||||
background:
|
||||
radial-gradient(1200px 600px at 10% -10%, #8f2d2d 0%, transparent 60%),
|
||||
radial-gradient(900px 500px at 110% 110%, #3b1010 0%, transparent 60%),
|
||||
#2a0d0d;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
color: var(--bc-ink);
|
||||
}
|
||||
|
||||
/* Card */
|
||||
.bc {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
background: #fff;
|
||||
border-radius: 28px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
/* Banner */
|
||||
.bc-banner {
|
||||
position: relative;
|
||||
height: 130px;
|
||||
background: linear-gradient(135deg, var(--bc-accent) 0%, var(--bc-accent-2) 100%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bc-ring {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
.bc-ring--1 { width: 220px; height: 220px; right: -60px; top: -90px; }
|
||||
.bc-ring--2 { width: 140px; height: 140px; right: -20px; top: -50px; }
|
||||
.bc-ring--3 { width: 300px; height: 300px; left: -140px; bottom: -190px; }
|
||||
|
||||
.bc-org {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 22px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
/* Identity */
|
||||
.bc-identity {
|
||||
text-align: center;
|
||||
padding: 0 24px 4px;
|
||||
margin-top: -60px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bc-avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
border: 5px solid #fff;
|
||||
box-shadow: 0 8px 20px -6px rgba(0, 0, 0, 0.35);
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.bc-name {
|
||||
margin: 14px 0 2px;
|
||||
font-size: 1.65rem;
|
||||
line-height: 1.15;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.bc-title {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bc-accent);
|
||||
}
|
||||
|
||||
/* Save button */
|
||||
.bc-section {
|
||||
padding: 20px 24px 8px;
|
||||
}
|
||||
|
||||
.bc-save {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.6rem;
|
||||
padding: 0.9rem 1rem;
|
||||
border: 0;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, var(--bc-accent) 0%, var(--bc-accent-2) 100%);
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 10px 20px -10px var(--bc-accent);
|
||||
transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;
|
||||
}
|
||||
.bc-save:hover { transform: translateY(-1px); box-shadow: 0 14px 24px -10px var(--bc-accent); }
|
||||
.bc-save:active { transform: translateY(0); }
|
||||
.bc-save:disabled { opacity: 0.6; cursor: progress; }
|
||||
|
||||
/* Rows */
|
||||
.bc-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 8px 12px 4px;
|
||||
}
|
||||
|
||||
.bc-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.bc-row:hover { background: #f7f7f9; }
|
||||
|
||||
.bc-row-icon {
|
||||
flex: none;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background: color-mix(in srgb, var(--bc-accent) 10%, white);
|
||||
color: var(--bc-accent);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.bc-row-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.bc-row-label {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bc-muted);
|
||||
}
|
||||
|
||||
.bc-row-value {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
color: var(--bc-ink);
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
a.bc-row-value:hover { color: var(--bc-accent); }
|
||||
|
||||
.bc-copy {
|
||||
flex: none;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1.5px solid var(--bc-line);
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
color: var(--bc-muted);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.bc-copy:hover { border-color: var(--bc-accent); color: var(--bc-accent); }
|
||||
.bc-copy:active { transform: scale(0.92); }
|
||||
.bc-copy--done { background: #2e9e5b; border-color: #2e9e5b; color: #fff; }
|
||||
.bc-copy--done:hover { background: #2e9e5b; border-color: #2e9e5b; color: #fff; }
|
||||
|
||||
/* QR */
|
||||
.bc-qr {
|
||||
padding: 24px 20px 18px;
|
||||
text-align: center;
|
||||
border-top: 1px dashed var(--bc-line);
|
||||
}
|
||||
|
||||
.bc-qr-box {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin: 0 auto;
|
||||
padding: 10px;
|
||||
border-radius: 20px;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 1.5px var(--bc-line), 0 12px 24px -12px rgba(0, 0, 0, 0.25);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bc-qr-box img { width: 100%; height: 100%; display: block; border-radius: 8px; }
|
||||
|
||||
.bc-qr-caption {
|
||||
margin-top: 14px;
|
||||
font-size: 0.82rem;
|
||||
color: var(--bc-muted);
|
||||
}
|
||||
|
||||
.bc-qr-link {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--bc-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
.bc-qr-link:hover { text-decoration: underline; }
|
||||
|
||||
/* Toast */
|
||||
.bc-toast {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 28px;
|
||||
transform: translate(-50%, 20px);
|
||||
padding: 0.65rem 1.1rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(28, 28, 30, 0.92);
|
||||
color: #fff;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s, transform 0.2s;
|
||||
}
|
||||
.bc-toast--show { opacity: 1; transform: translate(-50%, 0); }
|
||||
|
||||
@media (max-width: 440px) {
|
||||
.bc-page { padding: 0; align-items: stretch; }
|
||||
.bc { max-width: none; border-radius: 0; }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import Foot from '@/components/foot.vue';
|
||||
import Profile from '@/components/profile.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Spacer -->
|
||||
<div class="w-100" style="height: min(75vh, 500px)"></div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="cap-width mb-3 bg-white">
|
||||
<!-- Header -->
|
||||
<header class="px-4 d-flex justify-content-center justify-content-sm-start" style="background: #732222">
|
||||
<profile style="transform: translateY(-33%)" />
|
||||
</header>
|
||||
|
||||
<!-- Body -->
|
||||
<main class="p-3">
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<foot />
|
||||
</div>
|
||||
|
||||
<!-- Spacer -->
|
||||
<div class="d-none d-lg-block w-100" style="height: 40px"></div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user