diff --git a/index.html b/index.html index 5ed1c5b..30f9e3f 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,7 @@ + Atlas diff --git a/package-lock.json b/package-lock.json index 8bca099..97ae492 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,18 @@ "name": "image-registry", "version": "0.0.0", "dependencies": { + "@highlightjs/vue-plugin": "^2.1.0", + "axios": "^1.4.0", + "highlight.js": "^11.8.0", + "js-yaml": "^4.1.0", "vue": "^3.2.47", - "vue-router": "^4.1.6" + "vue-highlightjs": "^1.3.3", + "vue-router": "^4.1.6", + "vuetify": "^3.3.11" }, "devDependencies": { - "@types/node": "^18.14.2", + "@types/js-yaml": "^4.0.5", + "@types/node": "^18.17.1", "@vitejs/plugin-vue": "^4.0.0", "@vue/tsconfig": "^0.1.3", "npm-run-all": "^4.1.5", @@ -384,10 +391,25 @@ "node": ">=12" } }, + "node_modules/@highlightjs/vue-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz", + "integrity": "sha512-E+bmk4ncca+hBEYRV2a+1aIzIV0VSY/e5ArjpuSN9IO7wBJrzUE2u4ESCwrbQD7sAy+jWQjkV5qCCWgc+pu7CQ==", + "peerDependencies": { + "highlight.js": "^11.0.1", + "vue": "^3" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true + }, "node_modules/@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "version": "18.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.1.tgz", + "integrity": "sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==", "dev": true }, "node_modules/@vitejs/plugin-vue": { @@ -614,6 +636,11 @@ "node": ">=4" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -627,6 +654,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -639,6 +671,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -697,6 +739,17 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -746,6 +799,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -885,6 +946,25 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -894,6 +974,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -1094,6 +1187,14 @@ "he": "bin/he" } }, + "node_modules/highlight.js": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", + "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -1323,6 +1424,17 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -1361,6 +1473,25 @@ "node": ">= 0.10.0" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1568,6 +1699,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -1866,7 +2002,7 @@ "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1967,6 +2103,14 @@ "@vue/shared": "3.2.47" } }, + "node_modules/vue-highlightjs": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/vue-highlightjs/-/vue-highlightjs-1.3.3.tgz", + "integrity": "sha512-NaBfeVVa5rbKw9bNU0ajAcDmKOdzT+XuBZwYKFbEGSGFKGOc5wYerMY3R4WQrnlLD7BkG9tIIusqemKnIgQ7MA==", + "dependencies": { + "highlight.js": "*" + } + }, "node_modules/vue-router": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz", @@ -2007,6 +2151,39 @@ "typescript": "*" } }, + "node_modules/vuetify": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.3.11.tgz", + "integrity": "sha512-hlbSgxXCcEu4VelJMRHKhvLpaitLsruHvdvolbqbCS6Z64XoulUDw3CQ9ay6WGBL7uckmop0bUYidD2d8mN0UQ==", + "engines": { + "node": "^12.20 || >=14.13" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "typescript": ">=4.7", + "vite-plugin-vuetify": "^1.0.0-alpha.12", + "vue": "^3.2.0", + "vue-i18n": "^9.0.0", + "webpack-plugin-vuetify": "^2.0.0-alpha.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vite-plugin-vuetify": { + "optional": true + }, + "vue-i18n": { + "optional": true + }, + "webpack-plugin-vuetify": { + "optional": true + } + } + }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2216,10 +2393,22 @@ "dev": true, "optional": true }, + "@highlightjs/vue-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz", + "integrity": "sha512-E+bmk4ncca+hBEYRV2a+1aIzIV0VSY/e5ArjpuSN9IO7wBJrzUE2u4ESCwrbQD7sAy+jWQjkV5qCCWgc+pu7CQ==", + "requires": {} + }, + "@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true + }, "@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "version": "18.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.1.tgz", + "integrity": "sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==", "dev": true }, "@vitejs/plugin-vue": { @@ -2423,6 +2612,11 @@ "color-convert": "^1.9.0" } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, "array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -2433,12 +2627,27 @@ "is-array-buffer": "^3.0.1" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true }, + "axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2491,6 +2700,14 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2531,6 +2748,11 @@ "object-keys": "^1.1.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2645,6 +2867,11 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2654,6 +2881,16 @@ "is-callable": "^1.1.3" } }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -2787,6 +3024,11 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "highlight.js": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", + "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==" + }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -2944,6 +3186,14 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -2976,6 +3226,19 @@ "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3113,6 +3376,11 @@ "source-map-js": "^1.0.2" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -3331,7 +3599,7 @@ "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true + "devOptional": true }, "unbox-primitive": { "version": "1.0.2", @@ -3379,6 +3647,14 @@ "@vue/shared": "3.2.47" } }, + "vue-highlightjs": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/vue-highlightjs/-/vue-highlightjs-1.3.3.tgz", + "integrity": "sha512-NaBfeVVa5rbKw9bNU0ajAcDmKOdzT+XuBZwYKFbEGSGFKGOc5wYerMY3R4WQrnlLD7BkG9tIIusqemKnIgQ7MA==", + "requires": { + "highlight.js": "*" + } + }, "vue-router": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz", @@ -3407,6 +3683,12 @@ "@volar/vue-typescript": "1.2.0" } }, + "vuetify": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.3.11.tgz", + "integrity": "sha512-hlbSgxXCcEu4VelJMRHKhvLpaitLsruHvdvolbqbCS6Z64XoulUDw3CQ9ay6WGBL7uckmop0bUYidD2d8mN0UQ==", + "requires": {} + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 61243b8..3afe1a9 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,18 @@ "type-check": "vue-tsc --noEmit" }, "dependencies": { + "@highlightjs/vue-plugin": "^2.1.0", + "axios": "^1.4.0", + "highlight.js": "^11.8.0", + "js-yaml": "^4.1.0", "vue": "^3.2.47", - "vue-router": "^4.1.6" + "vue-highlightjs": "^1.3.3", + "vue-router": "^4.1.6", + "vuetify": "^3.3.11" }, "devDependencies": { - "@types/node": "^18.14.2", + "@types/js-yaml": "^4.0.5", + "@types/node": "^18.17.1", "@vitejs/plugin-vue": "^4.0.0", "@vue/tsconfig": "^0.1.3", "npm-run-all": "^4.1.5", diff --git a/public/atlas-logo.svg b/public/atlas-logo.svg new file mode 100644 index 0000000..508c36c --- /dev/null +++ b/public/atlas-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index e217eba..6c479b6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,32 +1,42 @@ \ No newline at end of file diff --git a/src/assets/css/fonts.css b/src/assets/css/fonts.css index 1c9ff7b..b6582a0 100644 --- a/src/assets/css/fonts.css +++ b/src/assets/css/fonts.css @@ -1,3 +1,3 @@ -@import url('https://fonts.googleapis.com/css?family=Material+Icons'); +@import url('https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Two+Tone|Material+Icons+Round|Material+Icons+Sharp'); @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100;200;300;400;500;600;700;800;900&display=swap'); @import url('https://fonts.googleapis.com/css?family=Pacifico'); \ No newline at end of file diff --git a/src/assets/css/style.css b/src/assets/css/style.css index 70a5563..1ffd160 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -1,248 +1,111 @@ -body { - background-color: #F2F1ED; -} - body, input, button, select, textarea { font-family: 'Outfit', sans-serif; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 600; - margin: 0 0 20px 0; -} - -h1 { - font-size: 32px; -} - -h2 { - font-size: 28px; -} - -h3 { - font-size: 24px; -} - -h4 { - font-size: 20px; -} - -h5 { font-size: 18px; } -h6 { - font-size: 16px; -} - -a { - color: #000; -} - -header { - display: flex; - justify-content: space-between; - align-items: center; -} - -header ul { - display: flex; - gap: 20px; +.card-header-title { + gap: 10px; } -header ul li a { - font-size: 17px; - text-decoration: none; +table.is-bordered tr:last-child td, +.table.is-bordered tr:last-child th, +.table.is-narrow td, +.table.is-narrow th { + padding: 0.6em 0.5em; } -.logo { - font-family: "Pacifico", cursive; - font-size: 28px; +.table thead th { + font-weight: 500; } -.container { - max-width: 1200px; - margin: 0 auto; +pre.block { padding: 20px; + margin: 0 0 10px 0; + line-height: 1.3; } -header a { - text-decoration: none; -} - -header a:hover { - text-decoration: underline; -} - -.container-small { - max-width: 510px; -} - -.v-center { - height: 70vh; - justify-content: center; -} - -.search p { - font-size: 21px; -} - -.search.search-big { - text-align: center; - justify-content: center; - margin: 0 auto; -} - -.search-field { - background: #fff; - padding: 15px 20px 15px 56px; - display: flex; - justify-content: center; - border-radius: 6px; - gap: 10px; - box-shadow: 0 1px 1px 0px #d1cec5, 0 3px 10px 0 #e1dccc; - border: 0; - outline: none; - font-size: 20px; - flex: 1; -} - -.form-search { +.bar-chart { display: flex; - flex-direction: column; - align-items: stretch; + border: 1px solid #ccc; + height: 300px; + margin-top: 10px; + width: 100%; + overflow: hidden; } -.form-search .form-icon { - position: absolute; - margin: 16px; - pointer-events: none; +.bar-segment:hover { + background-color: #485fc7; } -.search { +.bar-segment { display: flex; - gap: 20px; - flex-direction: column; - align-items: stretch; - margin: 0 0 30px 0; + justify-content: center; + align-items: flex-end; + background-color: #3850b7; + color: #fff; + font-size: 14px; + width: calc(100% / var(--num-segments)); + transition: height 0.2s; + padding: 8px 10px; + flex-grow: 1; } -.table { - background: #fff; - border-radius: 6px; - box-shadow: 0 1px 1px 0px #d1cec5, 0 3px 10px 0 #e1dccc; - border-collapse: collapse; - width: 100%; +pre.block, +.card, +.bar-chart { + border-radius: 10px; } -.table td, -.table th, -.fabr-table-search { - border: 1px solid #d1cec5; - padding: 10px; -} -.table th, -.fabr-table-search { - background: #f2f1ed; - font-weight: 600; - text-align: initial; +.card-header { + box-shadow: none; } -.table tr:nth-child(even) { - background: #f9f8f5; +.card-content { + padding: 1rem 1.5rem; } -.table tr:hover { - background: #f2f1ed; +.badge { + display: inline-flex; + align-items: center; + gap: 5px; + background-color: #f2f2f2; + padding: 2px 8px; + border-radius: 20px; } -.panel { - background: #fff; - border-radius: 6px; - box-shadow: 0 1px 1px 0px #d1cec5, 0 3px 10px 0 #e1dccc; - padding: 20px; +p span.badge { + position: relative; + top: 4px; } -.panel-wrapper { +.flex-list { display: flex; - gap: 20px; flex-direction: column; + align-items: flex-start; + gap: 5px; } -.fabr-table-search { - width: 100%; - border-bottom: 0; - outline: none; -} - -small { - color: #686761; -} - -.panels { +.badges { display: flex; - flex-wrap: wrap; gap: 10px; - margin: 0 0 10px 0; -} - -.panels .panel { - flex-grow: 1; - overflow: auto; -} - -.panel h2 { - display: flex; align-items: center; - gap: 10px; - font-size: 23px; -} - -code.hljs { - border-radius: 6px; -} - -pre { - background-color: #282936; - padding: 20px; - color: #e9e9f4; - font-size: 18px; - font-family: monospace; - border-radius: 6px; -} - -b, -strong { - font-weight: 600; } -.boxed-list { +.flex-grid { display: flex; - flex-direction: column; - gap: 5px; + flex-wrap: wrap; + gap: 10px; } -.boxed-list-item { - display: flex; - align-items: center; - gap: 5px; +.flex-grid-item { + flex: 1; } -.boxed-list-wrapper { - background-color: #f2f1ed; - border-radius: 6px; - padding: 11px; - display: flex; - flex-direction: column; - gap: 5px; +.footer { + background-color: transparent; } \ No newline at end of file diff --git a/src/components/RecipeDetails.vue b/src/components/RecipeDetails.vue new file mode 100644 index 0000000..ccfc292 --- /dev/null +++ b/src/components/RecipeDetails.vue @@ -0,0 +1,141 @@ + + + \ No newline at end of file diff --git a/src/components/RecipeModules.vue b/src/components/RecipeModules.vue new file mode 100644 index 0000000..56fc809 --- /dev/null +++ b/src/components/RecipeModules.vue @@ -0,0 +1,171 @@ + + + diff --git a/src/components/RecipeRuns.vue b/src/components/RecipeRuns.vue new file mode 100644 index 0000000..044830b --- /dev/null +++ b/src/components/RecipeRuns.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/components/RecipeSnippet.vue b/src/components/RecipeSnippet.vue new file mode 100644 index 0000000..bab3654 --- /dev/null +++ b/src/components/RecipeSnippet.vue @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 0197de0..76f50b5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,14 @@ class AtlasConfig { - public static readonly registry = 'https://ghcr.io/vanilla-os'; - public static readonly title = 'Vanilla OS Registry'; + public static readonly registry = "https://raw.githubusercontent.com"; + public static readonly title = "Vanilla OS Registry"; + public static readonly repos = [ + "vanilla-os/pico-image", + "vanilla-os/core-image", + "vanilla-os/dev-image", + "vanilla-os/vso-image", + "vanilla-os/desktop-image", + "vanilla-os/nvidia-image", + ]; } export default AtlasConfig; diff --git a/src/core/helpers.ts b/src/core/helpers.ts new file mode 100644 index 0000000..d25edd1 --- /dev/null +++ b/src/core/helpers.ts @@ -0,0 +1,36 @@ +function getModuleTypeClass(type: string) { + switch (type) { + case "shell": + return "terminal"; + + case "apt": + return "inventory_2"; + case "dpkg": + return "inventory_2"; + case "dnf": + return "inventory_2"; + case "rpm": + return "inventory_2"; + case "yum": + return "inventory_2"; + + case "dpkg-buildpackage": + return "home_repair_service"; + case "rpm-build": + return "home_repair_service"; + + case "cmake": + return "build"; + case "go": + return "build"; + case "meson": + return "build"; + + default: + return "extension"; + } +} + +export default { + getModuleTypeClass +} \ No newline at end of file diff --git a/src/core/manager.ts b/src/core/manager.ts new file mode 100644 index 0000000..d6d5063 --- /dev/null +++ b/src/core/manager.ts @@ -0,0 +1,134 @@ +import axios from "axios"; +import * as yaml from "js-yaml"; +import type { VibRecipe, Module } from "@/core/models"; +import AtlasConfig from "@/config"; + +class AtlasManager { + private static readonly storageKey = "vibRecipes"; + + private static async fetchRecipeFromRepo( + repo: string + ): Promise { + const url = `${AtlasConfig.registry}/${repo}/main/recipe.yml`; + try { + const response = await axios.get(url); + return response.data; + } catch (error) { + console.error( + `Error fetching recipe.yml from ${repo}: ${(error as Error).message}` + ); + return null; + } + } + + private static async fetchModuleContentFromRepo( + repo: string, + path: string + ): Promise { + const url = `${AtlasConfig.registry}/${repo}/main/${path}.yml`; + try { + const response = await axios.get(url); + return response.data; + } catch (error) { + console.error( + `Error fetching module content from ${url}: ${(error as Error).message}` + ); + return null; + } + } + + private static saveToLocalStorage(recipes: VibRecipe[]): void { + const serializedRecipes = JSON.stringify(recipes); + localStorage.setItem(this.storageKey, serializedRecipes); + } + + private static getFromLocalStorage(): VibRecipe[] | null { + const serializedRecipes = localStorage.getItem(this.storageKey); + if (serializedRecipes) { + return JSON.parse(serializedRecipes) as VibRecipe[]; + } + return null; + } + + public static async getVibRecipes(): Promise { + console.log("Fetching VibRecipes..."); + + const cachedRecipes = this.getFromLocalStorage(); + if (cachedRecipes) { + console.log("Fetched VibRecipes from local storage."); + return cachedRecipes; + } + + const vibRecipes: VibRecipe[] = []; + + for (const repo of AtlasConfig.repos) { + console.log(`Fetching recipe.yml from ${repo}`); + const recipeYaml = await this.fetchRecipeFromRepo(repo); + if (recipeYaml !== null) { + try { + console.log(`Parsing recipe.yml from ${repo}`); + const recipeObject = yaml.load(recipeYaml) as VibRecipe; + recipeObject.snippet = recipeYaml; + const modules: Module[] = []; + + if (recipeObject.modules) { + for (const module of recipeObject.modules) { + if (module.includes) { + console.log( + `Fetching and processing included modules for ${repo}` + ); + for (const includePath of module.includes) { + const moduleContent = await this.fetchModuleContentFromRepo( + repo, + includePath + ); + if (moduleContent) { + try { + const includedModule = yaml.load(moduleContent) as Module; + includedModule.snippet = moduleContent; + modules.push(includedModule); + } catch (error) { + console.error( + `Error parsing included module from ${repo}: ${ + (error as Error).message + }` + ); + } + } + } + } else { + module.snippet = yaml.dump(module); + modules.push(module); + } + } + } + + recipeObject.id = repo.replace("/", "-"); + recipeObject.modules = modules; + vibRecipes.push(recipeObject); + } catch (error) { + console.error( + `Error parsing recipe.yml from ${repo}: ${(error as Error).message}` + ); + } + } + } + + this.saveToLocalStorage(vibRecipes); + + console.log("Finished fetching VibRecipes"); + return vibRecipes; + } + + public static async getVibRecipe(id: string): Promise { + const vibRecipes = await this.getVibRecipes(); + for (const recipe of vibRecipes) { + if (recipe.id === id) { + return recipe; + } + } + return null; + } +} + +export default AtlasManager; diff --git a/src/core/models.ts b/src/core/models.ts new file mode 100644 index 0000000..2699aca --- /dev/null +++ b/src/core/models.ts @@ -0,0 +1,36 @@ +interface Source { + packages?: string; + path?: string; + url?: string; + type?: string; + tag?: string; + commit?: string; +} + +interface Module { + name: string; + type: string; + path?: string; + source?: Source; + buildflags?: string[]; + buildvars?: string[]; + modules?: { [key: string]: Module }; + includes?: string[]; + commands?: string[]; + snippet?: string; +} + +interface VibRecipe { + id: string; + recipeObject: any; + base: string; + name: string; + singlelayer: boolean; + labels: { [key: string]: string }; + args: { [key: string]: string }; + runs: string[]; + modules: Module[]; + snippet: string; +} + +export type { VibRecipe, Module, Source }; diff --git a/src/core/registry.ts b/src/core/registry.ts deleted file mode 100644 index 0991356..0000000 --- a/src/core/registry.ts +++ /dev/null @@ -1,72 +0,0 @@ -import AtlasConfig from '@/config' -import { OciImage } from "@/core/image"; - - -class RegistryManager { - - async get_tags(image: string) { - const r = await fetch(`${AtlasConfig.registry}/v2/${image}/tags/list`); - if (!r.ok) return []; - - const json = await r.json(); - return json.tags as string[]; - } - - async get_manifest(image: string, tag: string) { - const r = await fetch(`${AtlasConfig.registry}/v2/${image}/manifests/${tag}`); - if (!r.ok) return {}; - - return await r.json(); - } - - async get_config(image: string, digest: string) { - const r = await fetch(`${AtlasConfig.registry}/v2/${image}/blobs/${digest}`); - if (!r.ok) return {}; - - return await r.json(); - } - - async get_image(image: string, tag: string) : Promise{ - const manifest = await this.get_manifest(image, tag); - const config = await this.get_config(image, manifest.config.digest); - const ociImage = new OciImage(); - const totalSize = manifest.layers.reduce((a: number, b: any) => a + b.size, 0); - - ociImage.name = image; - ociImage.size = totalSize; - ociImage.create_date = new Date(config.history[0].created); - ociImage.digest = manifest.config.digest; - ociImage.tag = tag; - ociImage.architecture = config.architecture; - ociImage.os = config.os; - ociImage.config = config.config; - ociImage.history = config.history; - ociImage.layers = manifest.layers; - - return ociImage; - } - - - - async get_images() { - const images: OciImage[] = []; - - const r = await fetch(`${AtlasConfig.registry}/v2/_catalog`); - if (!r.ok) return images; - - const json = await r.json(); - const repositories: string[] = json["repositories"]; - - for (const image of repositories) { - const tags = await this.get_tags(image); - - for (const tag of tags) { - images.push(await this.get_image(image, tag)); - } - } - return images; - } - - } - -export { RegistryManager, OciImage }; \ No newline at end of file diff --git a/src/core/yaml-highlight.ts b/src/core/yaml-highlight.ts new file mode 100644 index 0000000..c1d6868 --- /dev/null +++ b/src/core/yaml-highlight.ts @@ -0,0 +1,14 @@ +import hljs from 'highlight.js/lib/core'; +import yaml from 'highlight.js/lib/languages/yaml'; +import 'highlight.js/styles/atom-one-dark.css'; + +hljs.registerLanguage('yaml', yaml); + +export default { + mounted(el: HTMLElement) { + const code = el.innerText; + const highlightedCode = hljs.highlight('yaml', code).value; + el.innerHTML = highlightedCode; + el.classList.add('hljs'); + }, +}; diff --git a/src/main.ts b/src/main.ts index 4061701..837da52 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,13 @@ -import { createApp } from 'vue' -import App from './App.vue' -import router from './router' +import { createApp } from "vue"; +import App from "./App.vue"; +import router from "./router"; +import yamlHighlight from '@/core//yaml-highlight'; -import './assets/css/index.css' +import "./assets/css/index.css"; -const app = createApp(App) +const app = createApp(App); -app.use(router) +app.use(router); +app.directive('highlight-yaml', yamlHighlight); -app.mount('#app') +app.mount("#app"); diff --git a/src/router/index.ts b/src/router/index.ts index e259575..80be006 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,28 +1,28 @@ -import { createRouter, createWebHistory } from 'vue-router' -import HomeView from '../views/HomeView.vue' -import ImageView from '../views/ImageView.vue' -import AtlasConfig from '@/config' +import { createRouter, createWebHistory } from "vue-router"; +import HomeView from "../views/HomeView.vue"; +import RecipeView from "../views/RecipeView.vue"; +import AtlasConfig from "@/config"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { - path: '/', - name: 'Home', - component: HomeView + path: "/", + name: "home", + component: HomeView, }, { - path: '/image/:image/tag/:tag', - name: 'Image', - component: ImageView - } - ] -}) + path: "/recipe/:id", + name: "recipe", + component: RecipeView, + }, + ], +}); router.beforeEach((to, from, next) => { - const suffix = AtlasConfig.title ? ` ${AtlasConfig.title}` : ''; + const suffix = AtlasConfig.title ? ` ${AtlasConfig.title}` : ""; document.title = to.meta.title ? to.meta.title + suffix : suffix; next(); }); -export default router +export default router; diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index c927453..8b778d4 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -1,36 +1,65 @@ \ No newline at end of file +export default defineComponent({ + name: 'HomeView', + data() { + return { + recipes: [] as VibRecipe[], + }; + }, + async mounted() { + try { + this.recipes = await AtlasManager.getVibRecipes(); + } catch (error) { + console.error('Error fetching recipes:', error); + } + }, +}); + diff --git a/src/views/ImageView.vue b/src/views/ImageView.vue deleted file mode 100644 index 2650863..0000000 --- a/src/views/ImageView.vue +++ /dev/null @@ -1,127 +0,0 @@ - - - \ No newline at end of file diff --git a/src/views/RecipeView.vue b/src/views/RecipeView.vue new file mode 100644 index 0000000..2edf98f --- /dev/null +++ b/src/views/RecipeView.vue @@ -0,0 +1,86 @@ + + + \ No newline at end of file