@k-4u/resource-mapper-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aws-icons-last-updated +1 -0
- package/.svelte-kit/ambient.d.ts +263 -0
- package/.svelte-kit/generated/client/app.js +31 -0
- package/.svelte-kit/generated/client/matchers.js +1 -0
- package/.svelte-kit/generated/client/nodes/0.js +1 -0
- package/.svelte-kit/generated/client/nodes/1.js +1 -0
- package/.svelte-kit/generated/client/nodes/2.js +3 -0
- package/.svelte-kit/generated/client/nodes/3.js +3 -0
- package/.svelte-kit/generated/client-optimized/app.js +31 -0
- package/.svelte-kit/generated/client-optimized/matchers.js +1 -0
- package/.svelte-kit/generated/client-optimized/nodes/0.js +1 -0
- package/.svelte-kit/generated/client-optimized/nodes/1.js +1 -0
- package/.svelte-kit/generated/client-optimized/nodes/2.js +3 -0
- package/.svelte-kit/generated/client-optimized/nodes/3.js +3 -0
- package/.svelte-kit/generated/root.js +3 -0
- package/.svelte-kit/generated/root.svelte +68 -0
- package/.svelte-kit/generated/server/internal.js +53 -0
- package/.svelte-kit/non-ambient.d.ts +43 -0
- package/.svelte-kit/output/client/.vite/manifest.json +175 -0
- package/.svelte-kit/output/client/_app/immutable/assets/0.Czt_67iE.css +1 -0
- package/.svelte-kit/output/client/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css +1 -0
- package/.svelte-kit/output/client/_app/immutable/assets/helpers.ysDrpaDf.css +1 -0
- package/.svelte-kit/output/client/_app/immutable/assets/libavoid.DQJapW5w.wasm +0 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/BlLuv0eP.js +46 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/CSBHmwYv.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/CTCi5ueQ.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/CfOzjaik.js +2 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/D4PdvFNs.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/DXgP-QUS.js +2 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/DlbDC5An.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/wRWe7aK9.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/entry/app.ConrMuHl.js +2 -0
- package/.svelte-kit/output/client/_app/immutable/entry/start.Bm6FyGme.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/0.d3cL-ETU.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/1.D6z9rPGv.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/2.CLD-8chl.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/3.DXYeBoel.js +1 -0
- package/.svelte-kit/output/client/_app/version.json +1 -0
- package/.svelte-kit/output/client/libavoid.wasm +0 -0
- package/.svelte-kit/output/client/static/robots.txt +3 -0
- package/.svelte-kit/output/prerendered/dependencies/_app/env.js +1 -0
- package/.svelte-kit/output/server/.vite/manifest.json +224 -0
- package/.svelte-kit/output/server/_app/immutable/assets/LoadingOverlay.DBbe6V8W.css +1 -0
- package/.svelte-kit/output/server/_app/immutable/assets/_layout.Czt_67iE.css +1 -0
- package/.svelte-kit/output/server/_app/immutable/assets/_page.D9P41uDZ.css +1 -0
- package/.svelte-kit/output/server/chunks/ErrorDisplay.js +59 -0
- package/.svelte-kit/output/server/chunks/LoadingOverlay.js +12 -0
- package/.svelte-kit/output/server/chunks/LoadingOverlay.svelte_svelte_type_style_lang.js +1671 -0
- package/.svelte-kit/output/server/chunks/connections.js +33 -0
- package/.svelte-kit/output/server/chunks/diagram.js +7 -0
- package/.svelte-kit/output/server/chunks/environment.js +34 -0
- package/.svelte-kit/output/server/chunks/equality.js +57 -0
- package/.svelte-kit/output/server/chunks/exports.js +174 -0
- package/.svelte-kit/output/server/chunks/false.js +4 -0
- package/.svelte-kit/output/server/chunks/index.js +59 -0
- package/.svelte-kit/output/server/chunks/index2.js +2939 -0
- package/.svelte-kit/output/server/chunks/index3.js +20 -0
- package/.svelte-kit/output/server/chunks/internal.js +1017 -0
- package/.svelte-kit/output/server/chunks/shared.js +770 -0
- package/.svelte-kit/output/server/chunks/utils.js +43 -0
- package/.svelte-kit/output/server/entries/pages/_error.svelte.js +64 -0
- package/.svelte-kit/output/server/entries/pages/_layout.svelte.js +65 -0
- package/.svelte-kit/output/server/entries/pages/_page.svelte.js +3991 -0
- package/.svelte-kit/output/server/entries/pages/_page.ts.js +30 -0
- package/.svelte-kit/output/server/entries/pages/group/_groupId_/_page.svelte.js +67 -0
- package/.svelte-kit/output/server/entries/pages/group/_groupId_/_page.ts.js +47 -0
- package/.svelte-kit/output/server/index.js +3747 -0
- package/.svelte-kit/output/server/internal.js +13 -0
- package/.svelte-kit/output/server/manifest-full.js +47 -0
- package/.svelte-kit/output/server/manifest.js +47 -0
- package/.svelte-kit/output/server/nodes/0.js +8 -0
- package/.svelte-kit/output/server/nodes/1.js +8 -0
- package/.svelte-kit/output/server/nodes/2.js +10 -0
- package/.svelte-kit/output/server/nodes/3.js +10 -0
- package/.svelte-kit/output/server/remote-entry.js +557 -0
- package/.svelte-kit/tsconfig.json +61 -0
- package/.svelte-kit/types/route_meta_data.json +8 -0
- package/.svelte-kit/types/src/routes/$types.d.ts +26 -0
- package/.svelte-kit/types/src/routes/group/[groupId]/$types.d.ts +21 -0
- package/.svelte-kit/types/src/routes/group/[groupId]/proxy+page.ts +49 -0
- package/.svelte-kit/types/src/routes/proxy+page.ts +33 -0
- package/build/_app/env.js +1 -0
- package/build/_app/env.js.br +1 -0
- package/build/_app/env.js.gz +0 -0
- package/build/_app/immutable/assets/0.Czt_67iE.css +1 -0
- package/build/_app/immutable/assets/0.Czt_67iE.css.br +0 -0
- package/build/_app/immutable/assets/0.Czt_67iE.css.gz +0 -0
- package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css +1 -0
- package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css.br +0 -0
- package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css.gz +0 -0
- package/build/_app/immutable/assets/helpers.ysDrpaDf.css +1 -0
- package/build/_app/immutable/assets/helpers.ysDrpaDf.css.br +0 -0
- package/build/_app/immutable/assets/helpers.ysDrpaDf.css.gz +0 -0
- package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm +0 -0
- package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm.br +0 -0
- package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm.gz +0 -0
- package/build/_app/immutable/chunks/BlLuv0eP.js +46 -0
- package/build/_app/immutable/chunks/BlLuv0eP.js.br +0 -0
- package/build/_app/immutable/chunks/BlLuv0eP.js.gz +0 -0
- package/build/_app/immutable/chunks/CSBHmwYv.js +1 -0
- package/build/_app/immutable/chunks/CSBHmwYv.js.br +0 -0
- package/build/_app/immutable/chunks/CSBHmwYv.js.gz +0 -0
- package/build/_app/immutable/chunks/CTCi5ueQ.js +1 -0
- package/build/_app/immutable/chunks/CTCi5ueQ.js.br +0 -0
- package/build/_app/immutable/chunks/CTCi5ueQ.js.gz +0 -0
- package/build/_app/immutable/chunks/CfOzjaik.js +2 -0
- package/build/_app/immutable/chunks/CfOzjaik.js.br +0 -0
- package/build/_app/immutable/chunks/CfOzjaik.js.gz +0 -0
- package/build/_app/immutable/chunks/D4PdvFNs.js +1 -0
- package/build/_app/immutable/chunks/D4PdvFNs.js.br +0 -0
- package/build/_app/immutable/chunks/D4PdvFNs.js.gz +0 -0
- package/build/_app/immutable/chunks/DXgP-QUS.js +2 -0
- package/build/_app/immutable/chunks/DXgP-QUS.js.br +0 -0
- package/build/_app/immutable/chunks/DXgP-QUS.js.gz +0 -0
- package/build/_app/immutable/chunks/DlbDC5An.js +1 -0
- package/build/_app/immutable/chunks/DlbDC5An.js.br +0 -0
- package/build/_app/immutable/chunks/DlbDC5An.js.gz +0 -0
- package/build/_app/immutable/chunks/wRWe7aK9.js +1 -0
- package/build/_app/immutable/chunks/wRWe7aK9.js.br +0 -0
- package/build/_app/immutable/chunks/wRWe7aK9.js.gz +0 -0
- package/build/_app/immutable/entry/app.ConrMuHl.js +2 -0
- package/build/_app/immutable/entry/app.ConrMuHl.js.br +0 -0
- package/build/_app/immutable/entry/app.ConrMuHl.js.gz +0 -0
- package/build/_app/immutable/entry/start.Bm6FyGme.js +1 -0
- package/build/_app/immutable/entry/start.Bm6FyGme.js.br +2 -0
- package/build/_app/immutable/entry/start.Bm6FyGme.js.gz +0 -0
- package/build/_app/immutable/nodes/0.d3cL-ETU.js +1 -0
- package/build/_app/immutable/nodes/0.d3cL-ETU.js.br +0 -0
- package/build/_app/immutable/nodes/0.d3cL-ETU.js.gz +0 -0
- package/build/_app/immutable/nodes/1.D6z9rPGv.js +1 -0
- package/build/_app/immutable/nodes/1.D6z9rPGv.js.br +0 -0
- package/build/_app/immutable/nodes/1.D6z9rPGv.js.gz +0 -0
- package/build/_app/immutable/nodes/2.CLD-8chl.js +1 -0
- package/build/_app/immutable/nodes/2.CLD-8chl.js.br +0 -0
- package/build/_app/immutable/nodes/2.CLD-8chl.js.gz +0 -0
- package/build/_app/immutable/nodes/3.DXYeBoel.js +1 -0
- package/build/_app/immutable/nodes/3.DXYeBoel.js.br +0 -0
- package/build/_app/immutable/nodes/3.DXYeBoel.js.gz +0 -0
- package/build/_app/version.json +1 -0
- package/build/_app/version.json.br +0 -0
- package/build/_app/version.json.gz +0 -0
- package/build/index.html +34 -0
- package/build/index.html.br +0 -0
- package/build/index.html.gz +0 -0
- package/build/libavoid.wasm +0 -0
- package/build/libavoid.wasm.br +0 -0
- package/build/libavoid.wasm.gz +0 -0
- package/build/static/robots.txt +3 -0
- package/coverage/coverage-final.json +6 -0
- package/coverage/coverage-summary.json +7 -0
- package/coverage/junit.xml +57 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +131 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/stores/index.html +116 -0
- package/coverage/lcov-report/stores/routingStore.ts.html +781 -0
- package/coverage/lcov-report/utils/flow/helpers.ts.html +127 -0
- package/coverage/lcov-report/utils/flow/index.html +161 -0
- package/coverage/lcov-report/utils/flow/layout.ts.html +805 -0
- package/coverage/lcov-report/utils/flow/serviceIds.ts.html +97 -0
- package/coverage/lcov-report/utils/flow/servicesGraph.ts.html +859 -0
- package/coverage/lcov.info +646 -0
- package/coverage/test-results.json +1 -0
- package/data/services/api/api-gateway.yaml +18 -0
- package/data/services/api/group-info.yaml +7 -0
- package/data/services/api/lambda-orders.yaml +21 -0
- package/data/services/api/lambda-products.yaml +15 -0
- package/data/services/api/lambda-users.yaml +15 -0
- package/data/services/compute/alb.yaml +15 -0
- package/data/services/compute/ecs-inventory.yaml +16 -0
- package/data/services/compute/ecs-notification.yaml +15 -0
- package/data/services/compute/group-info.yaml +7 -0
- package/data/services/data/dynamodb-notifications.yaml +12 -0
- package/data/services/data/dynamodb-orders.yaml +9 -0
- package/data/services/data/dynamodb-products.yaml +9 -0
- package/data/services/data/dynamodb-users.yaml +9 -0
- package/data/services/data/group-info.yaml +7 -0
- package/data/services/data/rds-postgres.yaml +9 -0
- package/data/services/data/redis.yaml +10 -0
- package/data/services/frontend/cloudfront.yaml +12 -0
- package/data/services/frontend/group-info.yaml +7 -0
- package/data/services/frontend/route53.yaml +15 -0
- package/data/services/frontend/s3-website.yaml +9 -0
- package/data/teams/cloud-shepherds.yaml +15 -0
- package/data/teams/data-wizards.yaml +15 -0
- package/data/teams/interface-architects.yaml +19 -0
- package/e2e/demo.test.ts +54 -0
- package/e2e/header-toolbar.simple.spec.ts +0 -0
- package/e2e/header-toolbar.spec.ts +53 -0
- package/e2e/layout.spec.ts +30 -0
- package/package.json +69 -0
- package/playwright.config.ts +10 -0
- package/plugins/mapper-data-plugin.ts +32 -0
- package/project.json +23 -0
- package/src/app.css +125 -0
- package/src/app.d.ts +31 -0
- package/src/app.html +11 -0
- package/src/lib/assets/favicon.svg +19 -0
- package/src/lib/components/EmptyState.svelte +37 -0
- package/src/lib/components/ErrorDisplay.svelte +82 -0
- package/src/lib/components/FlowCanvas.svelte +223 -0
- package/src/lib/components/GenericSidebarCard.svelte +44 -0
- package/src/lib/components/GroupDetailSidebar.svelte +31 -0
- package/src/lib/components/Header.svelte +57 -0
- package/src/lib/components/Legend.svelte +25 -0
- package/src/lib/components/LoadingOverlay.svelte +42 -0
- package/src/lib/components/LoadingSpinner.svelte +10 -0
- package/src/lib/components/ServiceDetailSidebar.svelte +90 -0
- package/src/lib/components/TeamContactCard.svelte +166 -0
- package/src/lib/components/flow/ExternalNode.svelte +45 -0
- package/src/lib/components/flow/MainGroupNode.svelte +24 -0
- package/src/lib/components/flow/ServiceGroupNode.svelte +17 -0
- package/src/lib/components/flow/ServiceNode.svelte +40 -0
- package/src/lib/components/flow/SnakeEdge.svelte +206 -0
- package/src/lib/components/flow/index.ts +6 -0
- package/src/lib/components/index.ts +12 -0
- package/src/lib/data/connections.ts +26 -0
- package/src/lib/data/groups.ts +11 -0
- package/src/lib/data/services.ts +12 -0
- package/src/lib/data/teams.ts +11 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/state/theme.svelte.ts +21 -0
- package/src/lib/stores/diagram.ts +6 -0
- package/src/lib/stores/routingStore.test.ts +133 -0
- package/src/lib/stores/routingStore.ts +232 -0
- package/src/lib/utils/awsIcons.ts +117 -0
- package/src/lib/utils/flow/groupOverviewGraph.ts +73 -0
- package/src/lib/utils/flow/helpers.ts +14 -0
- package/src/lib/utils/flow/layout.test.ts +271 -0
- package/src/lib/utils/flow/layout.ts +240 -0
- package/src/lib/utils/flow/serviceIds.ts +5 -0
- package/src/lib/utils/flow/servicesGraph.test.ts +119 -0
- package/src/lib/utils/flow/servicesGraph.ts +258 -0
- package/src/routes/+error.svelte +36 -0
- package/src/routes/+layout.svelte +27 -0
- package/src/routes/+page.svelte +81 -0
- package/src/routes/+page.ts +31 -0
- package/src/routes/group/[groupId]/+page.svelte +102 -0
- package/src/routes/group/[groupId]/+page.ts +48 -0
- package/src/routes/layout.css +0 -0
- package/static/static/robots.txt +3 -0
- package/svelte.config.js +28 -0
- package/tailwind.config.js +12 -0
- package/tsconfig.json +22 -0
- package/vite.config.ts +81 -0
package/src/app.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// See https://svelte.dev/docs/kit/types#app.d.ts
|
|
2
|
+
// for information about these interfaces
|
|
3
|
+
declare global {
|
|
4
|
+
namespace App {
|
|
5
|
+
// interface Error {}
|
|
6
|
+
// interface Locals {}
|
|
7
|
+
// interface PageData {}
|
|
8
|
+
// interface PageState {}
|
|
9
|
+
// interface Platform {}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
console.log('Adding virtual:mapper-data module declaration');
|
|
14
|
+
declare global {
|
|
15
|
+
declare module 'virtual:mapper-data' {
|
|
16
|
+
import type {GroupInfo, GroupConnection, ServiceDefinition, ExternalGroupServices, Team} from '$shared/types';
|
|
17
|
+
|
|
18
|
+
const bakedData: {
|
|
19
|
+
groups: Record<string, GroupInfo>;
|
|
20
|
+
services: Record<string, ServiceDefinition>;
|
|
21
|
+
teams: Record<string, Team>;
|
|
22
|
+
groupConnections: GroupConnection[];
|
|
23
|
+
servicesByGroup: Record<string, ServiceDefinition[]>;
|
|
24
|
+
externalServices: Record<string, ExternalGroupServices[]>;
|
|
25
|
+
generatedAt: Date
|
|
26
|
+
}
|
|
27
|
+
export default bakedData;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {};
|
package/src/app.html
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
%sveltekit.head%
|
|
7
|
+
</head>
|
|
8
|
+
<body data-sveltekit-preload-data="hover" class="bg-white dark:bg-gray-900">
|
|
9
|
+
<div style="display: contents">%sveltekit.body%</div>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
|
|
3
|
+
<path d="M64 42V58M64 58H34V72M64 58H94V72" stroke="#38BDF8" stroke-width="4" stroke-linejoin="round" stroke-linecap="round"/>
|
|
4
|
+
|
|
5
|
+
<rect x="34" y="20" width="60" height="22" rx="6" fill="#92400E" stroke="#F59E0B" stroke-width="2"/>
|
|
6
|
+
<rect x="42" y="28" width="10" height="6" rx="1" fill="#F59E0B"/>
|
|
7
|
+
<rect x="76" y="28" width="10" height="6" rx="1" fill="#F59E0B"/>
|
|
8
|
+
|
|
9
|
+
<rect x="14" y="72" width="44" height="28" rx="6" fill="#065F46" stroke="#10B981" stroke-width="2"/>
|
|
10
|
+
<circle cx="26" cy="86" r="5" fill="#10B981" />
|
|
11
|
+
<rect x="36" y="83" width="14" height="6" rx="1" fill="#10B981"/>
|
|
12
|
+
|
|
13
|
+
<rect x="70" y="72" width="44" height="28" rx="6" fill="#065F46" stroke="#10B981" stroke-width="2"/>
|
|
14
|
+
<circle cx="82" cy="86" r="5" fill="#10B981"/>
|
|
15
|
+
<rect x="92" y="83" width="14" height="6" rx="1" fill="#10B981"/>
|
|
16
|
+
|
|
17
|
+
<rect x="22" y="55" width="16" height="8" rx="2" fill="#1E293B" stroke="#38BDF8" stroke-width="1"/>
|
|
18
|
+
<rect x="90" y="55" width="16" height="8" rx="2" fill="#1E293B" stroke="#38BDF8" stroke-width="1"/>
|
|
19
|
+
</svg>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '@iconify/svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
title?: string
|
|
6
|
+
message?: string
|
|
7
|
+
actionLabel?: string | null
|
|
8
|
+
onAction?: (() => void) | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
title = 'Nothing to show',
|
|
13
|
+
message = '',
|
|
14
|
+
actionLabel = null,
|
|
15
|
+
onAction = null
|
|
16
|
+
}: Props = $props()
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div class="flex h-full w-full items-center justify-center p-8">
|
|
20
|
+
<div class="max-w-md rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-8 text-center shadow">
|
|
21
|
+
<div class="flex items-center justify-center mb-4">
|
|
22
|
+
<Icon icon="mdi:package-variant" class="text-4xl text-gray-400 dark:text-gray-300" aria-hidden="true" />
|
|
23
|
+
</div>
|
|
24
|
+
<h2 class="big-title text-xl font-semibold text-gray-900 dark:text-gray-100">{title}</h2>
|
|
25
|
+
{#if message}
|
|
26
|
+
<p class="mt-2 text-base text-gray-700 dark:text-gray-300">{message}</p>
|
|
27
|
+
{/if}
|
|
28
|
+
{#if actionLabel && onAction}
|
|
29
|
+
<button
|
|
30
|
+
class="mt-6 w-full rounded-md bg-blue-600 py-2 text-white hover:bg-blue-500"
|
|
31
|
+
on:click={onAction}
|
|
32
|
+
>
|
|
33
|
+
{actionLabel}
|
|
34
|
+
</button>
|
|
35
|
+
{/if}
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '@iconify/svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
title = 'Something went wrong',
|
|
6
|
+
message = 'An unexpected error occurred.',
|
|
7
|
+
checkList = [],
|
|
8
|
+
technicalDetails = null,
|
|
9
|
+
onRetry = null,
|
|
10
|
+
onBack = null
|
|
11
|
+
} = $props<{
|
|
12
|
+
title?: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
checkList?: string[];
|
|
15
|
+
technicalDetails?: string | Record<string, unknown> | null;
|
|
16
|
+
onRetry?: (() => void) | null;
|
|
17
|
+
onBack?: (() => void) | null;
|
|
18
|
+
}>();
|
|
19
|
+
|
|
20
|
+
let formattedDetails = $derived(
|
|
21
|
+
technicalDetails
|
|
22
|
+
? typeof technicalDetails === 'string'
|
|
23
|
+
? technicalDetails
|
|
24
|
+
: JSON.stringify(technicalDetails, null, 2)
|
|
25
|
+
: ''
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
let copied = $state(false)
|
|
29
|
+
|
|
30
|
+
async function copyDetails() {
|
|
31
|
+
if (!formattedDetails) return
|
|
32
|
+
await navigator.clipboard.writeText(formattedDetails)
|
|
33
|
+
copied = true
|
|
34
|
+
setTimeout(() => (copied = false), 2000)
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<div class="flex h-full w-full items-center justify-center p-8">
|
|
39
|
+
<div class="w-full max-w-2xl rounded-2xl border border-red-500/40 bg-red-500/10 p-6 shadow">
|
|
40
|
+
<div class="flex items-center text-red-300">
|
|
41
|
+
<Icon icon="mdi:alert-circle-outline" class="mr-2 h-7 w-7 text-red-400" aria-hidden="true" />
|
|
42
|
+
<h2 class="big-title text-xl font-semibold">{title}</h2>
|
|
43
|
+
</div>
|
|
44
|
+
<p class="mt-2 text-base text-gray-700 dark:text-gray-200">{message}</p>
|
|
45
|
+
|
|
46
|
+
{#if checkList.length}
|
|
47
|
+
<div class="mt-4 rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-4">
|
|
48
|
+
<p class="text-sm font-semibold text-gray-700 dark:text-gray-300">Please check:</p>
|
|
49
|
+
<ul class="mt-2 list-disc space-y-1 pl-5 text-sm text-gray-600 dark:text-gray-400">
|
|
50
|
+
{#each checkList as item}
|
|
51
|
+
<li>{item}</li>
|
|
52
|
+
{/each}
|
|
53
|
+
</ul>
|
|
54
|
+
</div>
|
|
55
|
+
{/if}
|
|
56
|
+
|
|
57
|
+
{#if formattedDetails}
|
|
58
|
+
<div class="mt-4 rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-4">
|
|
59
|
+
<div class="flex items-center justify-between">
|
|
60
|
+
<span class="text-sm font-semibold text-gray-700 dark:text-gray-300">Technical details</span>
|
|
61
|
+
<button class="text-sm text-blue-600 dark:text-blue-400 hover:text-blue-500 dark:hover:text-blue-300" onclick={copyDetails}>
|
|
62
|
+
{copied ? 'Copied' : 'Copy'}
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
<pre class="mt-2 max-h-48 overflow-auto rounded bg-gray-50 dark:bg-black/40 p-3 text-xs text-gray-700 dark:text-gray-300">{formattedDetails}</pre>
|
|
66
|
+
</div>
|
|
67
|
+
{/if}
|
|
68
|
+
|
|
69
|
+
<div class="mt-6 flex flex-wrap gap-3">
|
|
70
|
+
{#if onRetry}
|
|
71
|
+
<button class="rounded-md bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-500" onclick={onRetry}>
|
|
72
|
+
Retry
|
|
73
|
+
</button>
|
|
74
|
+
{/if}
|
|
75
|
+
{#if onBack}
|
|
76
|
+
<button class="rounded-md border border-gray-300 dark:border-gray-700 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800" onclick={onBack}>
|
|
77
|
+
Back
|
|
78
|
+
</button>
|
|
79
|
+
{/if}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {onMount} from 'svelte'
|
|
3
|
+
import {
|
|
4
|
+
Background,
|
|
5
|
+
BackgroundVariant,
|
|
6
|
+
ControlButton,
|
|
7
|
+
Controls,
|
|
8
|
+
type Edge,
|
|
9
|
+
type EdgeTypes,
|
|
10
|
+
MiniMap,
|
|
11
|
+
type Node,
|
|
12
|
+
type NodeTypes,
|
|
13
|
+
Panel,
|
|
14
|
+
SvelteFlow
|
|
15
|
+
} from '@xyflow/svelte'
|
|
16
|
+
import {ExternalNode, Legend, MainGroupNode, ServiceGroupNode, ServiceNode, SnakeEdge} from '$lib/components'
|
|
17
|
+
import {layoutFlowGraph} from '$lib/utils/flow/layout'
|
|
18
|
+
import type {FlowGraphInput} from '$shared/flow-types'
|
|
19
|
+
import {logDiagramAction, showLegend} from '$lib/stores/diagram';
|
|
20
|
+
import Icon from "@iconify/svelte";
|
|
21
|
+
import {goto} from "$app/navigation";
|
|
22
|
+
import {theme} from '$lib/state/theme.svelte';
|
|
23
|
+
|
|
24
|
+
const DOUBLE_CLICK_THRESHOLD = 400
|
|
25
|
+
|
|
26
|
+
// Svelte 5 Props
|
|
27
|
+
let {
|
|
28
|
+
graph = null,
|
|
29
|
+
pending = false,
|
|
30
|
+
onnodeDoubleClick
|
|
31
|
+
} = $props<{
|
|
32
|
+
graph?: FlowGraphInput | null;
|
|
33
|
+
pending?: boolean;
|
|
34
|
+
onnodeDoubleClick?: (nodeId: string) => void;
|
|
35
|
+
}>();
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const nodeTypes = {
|
|
39
|
+
mainGroup: MainGroupNode,
|
|
40
|
+
service: ServiceNode,
|
|
41
|
+
external: ExternalNode,
|
|
42
|
+
serviceGroup: ServiceGroupNode
|
|
43
|
+
} as NodeTypes
|
|
44
|
+
const edgeTypes = {snake: SnakeEdge} as EdgeTypes
|
|
45
|
+
|
|
46
|
+
// Svelte 5 State
|
|
47
|
+
let nodes: Node[] = $state([])
|
|
48
|
+
let edges: Edge[] = $state([])
|
|
49
|
+
|
|
50
|
+
let emptyStateLabel = $state('No diagram available.')
|
|
51
|
+
let lastClickedNode: string | null = $state(null)
|
|
52
|
+
let lastClickTimestamp = $state(0)
|
|
53
|
+
let layoutError = $state('')
|
|
54
|
+
let layoutBusy = $state(false)
|
|
55
|
+
let graphReady = $state(false)
|
|
56
|
+
let currentSignature = $state('')
|
|
57
|
+
|
|
58
|
+
// Svelte 5 Derived
|
|
59
|
+
let hasGraphData = $derived(!!graph && graph.serviceNodes.length > 0)
|
|
60
|
+
let showOverlay = $derived(pending || layoutBusy || !graphReady)
|
|
61
|
+
|
|
62
|
+
// TODO: This console.debug might be better as an effect or just removed if not needed for production
|
|
63
|
+
$effect(() => {
|
|
64
|
+
console.debug('[FlowCanvas] overlay state', {showOverlay, pending, layoutBusy, graphReady})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
onMount(() => {
|
|
68
|
+
let lastLogAction = 0;
|
|
69
|
+
// TODO: Consider converting logDiagramAction store to a rune-based state if possible, or use $effect
|
|
70
|
+
const unsubscribe = logDiagramAction.subscribe((n) => {
|
|
71
|
+
if (n !== lastLogAction) {
|
|
72
|
+
lastLogAction = n;
|
|
73
|
+
logDiagram();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return unsubscribe;
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// 3. Fix the Reactive Trigger
|
|
80
|
+
// Ensure this ONLY triggers when the parent passes a brand new graph
|
|
81
|
+
$effect(() => {
|
|
82
|
+
const newTargetSignature = graph?.signature ?? '';
|
|
83
|
+
if (newTargetSignature !== '' && newTargetSignature !== currentSignature) {
|
|
84
|
+
runLayout(graph, newTargetSignature);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
async function runLayout(sourceGraph: FlowGraphInput | null, signature: string) {
|
|
89
|
+
layoutError = ''
|
|
90
|
+
|
|
91
|
+
graphReady = false
|
|
92
|
+
|
|
93
|
+
console.debug('[FlowCanvas] runLayout start', {hasGraph: !!sourceGraph, signature})
|
|
94
|
+
if (!sourceGraph) {
|
|
95
|
+
nodes = []
|
|
96
|
+
edges = []
|
|
97
|
+
emptyStateLabel = pending ? 'Rendering diagram…' : 'No diagram available.'
|
|
98
|
+
currentSignature = signature
|
|
99
|
+
console.debug('[FlowCanvas] runLayout skipped - no graph data')
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
layoutBusy = true
|
|
103
|
+
try {
|
|
104
|
+
const result = await layoutFlowGraph(sourceGraph)
|
|
105
|
+
if (result.signature !== signature) {
|
|
106
|
+
console.debug('[FlowCanvas] runLayout result ignored due to stale signature', {
|
|
107
|
+
expected: signature,
|
|
108
|
+
received: result.signature
|
|
109
|
+
})
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
nodes = result.nodes as Node[]
|
|
113
|
+
edges = result.edges as Edge[]
|
|
114
|
+
|
|
115
|
+
currentSignature = result.signature;
|
|
116
|
+
|
|
117
|
+
graphReady = true
|
|
118
|
+
console.debug('[FlowCanvas] runLayout complete', {
|
|
119
|
+
nodeCount: result.nodes.length,
|
|
120
|
+
edgeCount: result.edges.length
|
|
121
|
+
})
|
|
122
|
+
} catch (error) {
|
|
123
|
+
layoutError = error instanceof Error ? error.message : 'Failed to lay out diagram.'
|
|
124
|
+
console.error('[FlowCanvas] layout error', error)
|
|
125
|
+
nodes = []
|
|
126
|
+
edges = []
|
|
127
|
+
graphReady = false
|
|
128
|
+
} finally {
|
|
129
|
+
layoutBusy = false
|
|
130
|
+
console.debug('[FlowCanvas] runLayout finished', {graphReady, layoutBusy})
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//TODO: This handleFlowNodeClick function seems to be unused in the template. If it's intended for Node components, consider passing it down.
|
|
135
|
+
function handleFlowNodeClick(event: CustomEvent<{ node?: { id?: string } }>) {
|
|
136
|
+
const nodeId = event.detail?.node?.id
|
|
137
|
+
if (!nodeId) return
|
|
138
|
+
const now = Date.now()
|
|
139
|
+
if (lastClickedNode === nodeId && now - lastClickTimestamp <= DOUBLE_CLICK_THRESHOLD) {
|
|
140
|
+
onnodeDoubleClick?.(nodeId)
|
|
141
|
+
lastClickedNode = null
|
|
142
|
+
lastClickTimestamp = 0
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
lastClickedNode = nodeId
|
|
146
|
+
lastClickTimestamp = now
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// TODO: logDiagram is only called from logDiagramAction subscription, verify if this is actually needed.
|
|
150
|
+
function logDiagram() {
|
|
151
|
+
console.log('[FlowCanvas] Diagram log triggered', {
|
|
152
|
+
nodes,
|
|
153
|
+
edges,
|
|
154
|
+
graph: graph
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function goHome() {
|
|
159
|
+
goto('/');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<div data-testid="flow-canvas"
|
|
165
|
+
class="flex h-full flex-auto flex-col overflow-hidden rounded-2xl border border-white/5 bg-black/20 shadow-xl">
|
|
166
|
+
{#if hasGraphData}
|
|
167
|
+
|
|
168
|
+
<SvelteFlow
|
|
169
|
+
bind:nodes
|
|
170
|
+
bind:edges
|
|
171
|
+
{nodeTypes}
|
|
172
|
+
{edgeTypes}
|
|
173
|
+
nodesDraggable
|
|
174
|
+
nodesConnectable={false}
|
|
175
|
+
panOnDrag
|
|
176
|
+
zoomOnScroll
|
|
177
|
+
selectionOnDrag={false}
|
|
178
|
+
colorMode={theme.isDark ? 'dark' : 'light'}
|
|
179
|
+
fitView
|
|
180
|
+
fitViewOptions={{ padding: 0.2 }}
|
|
181
|
+
>
|
|
182
|
+
<!-- Todo: See if we can use tailwind here-->
|
|
183
|
+
<Background bgColor={theme.isDark ? '#1f2937' : '#e5e7eb'} variant={BackgroundVariant.Dots} gap={24}/>
|
|
184
|
+
<MiniMap nodeColor={(n) => {
|
|
185
|
+
if (n.type === 'group') return '#2563eb'
|
|
186
|
+
if (n.type === 'service') return '#16a34a'
|
|
187
|
+
if (n.type === 'external') return '#d97706'
|
|
188
|
+
return '#9ca3af'
|
|
189
|
+
}} position="bottom-left"/>
|
|
190
|
+
<Controls position="top-left">
|
|
191
|
+
<!-- Go home button -->
|
|
192
|
+
<ControlButton onclick={goHome} title="Go Home" aria-label="Go to home page">
|
|
193
|
+
<Icon icon="mdi:home"/>
|
|
194
|
+
</ControlButton>
|
|
195
|
+
</Controls>
|
|
196
|
+
<Panel position="bottom-right">
|
|
197
|
+
{#if $showLegend && !showOverlay}
|
|
198
|
+
<Legend/>
|
|
199
|
+
{/if}
|
|
200
|
+
</Panel>
|
|
201
|
+
</SvelteFlow>
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
{:else}
|
|
205
|
+
<div class="flex h-full flex-col items-center justify-center gap-2 text-sm text-gray-400 dark:text-gray-300">
|
|
206
|
+
<span class="text-3xl" aria-hidden="true">🌀</span>
|
|
207
|
+
<span>{emptyStateLabel}</span>
|
|
208
|
+
</div>
|
|
209
|
+
{/if}
|
|
210
|
+
|
|
211
|
+
{#if showOverlay}
|
|
212
|
+
<div
|
|
213
|
+
data-testid="flow-loading-overlay"
|
|
214
|
+
class="pointer-events-none absolute inset-0 flex items-center justify-center bg-white/80 text-sm text-gray-700 dark:bg-black/70 dark:text-gray-200 backdrop-blur"
|
|
215
|
+
>
|
|
216
|
+
Rendering diagram…
|
|
217
|
+
</div>
|
|
218
|
+
{/if}
|
|
219
|
+
|
|
220
|
+
{#if layoutError}
|
|
221
|
+
<div class="border-t border-red-200 bg-red-100 px-4 py-2 text-sm text-red-700 dark:text-red-200 dark:bg-red-500/20 dark:border-red-500/40">{layoutError}</div>
|
|
222
|
+
{/if}
|
|
223
|
+
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
|
|
3
|
+
import type {Snippet} from "svelte";
|
|
4
|
+
|
|
5
|
+
const {title, subtitle, description, children, iconPath, iconAlt, classes} = $props<{
|
|
6
|
+
title: string,
|
|
7
|
+
subtitle?: string,
|
|
8
|
+
description?: string,
|
|
9
|
+
children?: Snippet,
|
|
10
|
+
iconPath?: string,
|
|
11
|
+
iconAlt?: string,
|
|
12
|
+
classes?: string
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<div class="sidebar-card {classes}">
|
|
19
|
+
<div class="flex items-start justify-between">
|
|
20
|
+
<div class="flex flex-col">
|
|
21
|
+
<div class="sidebar-card-title">{title}</div>
|
|
22
|
+
{#if subtitle}
|
|
23
|
+
<h3 class="sidebar-card-subtitle">{subtitle}</h3>
|
|
24
|
+
{/if}
|
|
25
|
+
</div>
|
|
26
|
+
{#if iconPath}
|
|
27
|
+
<img
|
|
28
|
+
src={iconPath}
|
|
29
|
+
alt={iconAlt}
|
|
30
|
+
class="h-10 w-10 rounded-lg bg-white/5 object-contain"
|
|
31
|
+
/>
|
|
32
|
+
{:else if iconAlt}
|
|
33
|
+
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-800 text-xs font-semibold text-gray-200">
|
|
34
|
+
{iconAlt}
|
|
35
|
+
</div>
|
|
36
|
+
{/if}
|
|
37
|
+
</div>
|
|
38
|
+
{#if description}
|
|
39
|
+
<p class="sidebar-card-contents">{description}</p>
|
|
40
|
+
{/if}
|
|
41
|
+
{#if children}
|
|
42
|
+
{@render children()}
|
|
43
|
+
{/if}
|
|
44
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {GroupInfo} from '$shared/types'
|
|
3
|
+
import {TeamContactCard, GenericSidebarCard} from '$lib/components'
|
|
4
|
+
import {useNodes} from '@xyflow/svelte';
|
|
5
|
+
|
|
6
|
+
const {groupMap, groups} = $props<{
|
|
7
|
+
groupMap: Record<string, string>;
|
|
8
|
+
groups: Record<string, GroupInfo>;
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const nodes = useNodes();
|
|
12
|
+
let selectedNodeId = $derived(nodes.current.find(n => n.selected)?.id ?? null);
|
|
13
|
+
|
|
14
|
+
let groupId = $derived(selectedNodeId ? (groupMap[selectedNodeId] ?? null) : null);
|
|
15
|
+
let groupInfo = $derived(groupId ? (groups[groupId] ?? null) : null);
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<aside data-testid="group-sidebar"
|
|
19
|
+
class="flex h-full flex-col basis-1/6 border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 text-gray-900 dark:text-gray-100 overflow-hidden rounded-2xl border shadow-xl p-3">
|
|
20
|
+
{#if !groupInfo}
|
|
21
|
+
<GenericSidebarCard title="No Group Selected" description="Select a group to view details." />
|
|
22
|
+
{:else}
|
|
23
|
+
<GenericSidebarCard title="Group" subtitle={groupInfo.name} description={groupInfo.description}/>
|
|
24
|
+
|
|
25
|
+
{#if groupInfo.teamId}
|
|
26
|
+
<div class="mt-4">
|
|
27
|
+
<TeamContactCard teamId={groupInfo.teamId}/>
|
|
28
|
+
</div>
|
|
29
|
+
{/if}
|
|
30
|
+
{/if}
|
|
31
|
+
</aside>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { goto } from '$app/navigation';
|
|
3
|
+
import { selectedGroup, showLegend, logDiagramAction } from '$lib/stores/diagram';
|
|
4
|
+
import { theme } from '$lib/state/theme.svelte';
|
|
5
|
+
import Icon from '@iconify/svelte';
|
|
6
|
+
|
|
7
|
+
function goHome() {
|
|
8
|
+
goto('/');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function toggleLegend() {
|
|
12
|
+
showLegend.update((v: boolean) => !v);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function toggleDarkMode() {
|
|
16
|
+
theme.toggle();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function logDiagram() {
|
|
20
|
+
logDiagramAction.update(n => n + 1);
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<header class="border-b border-gray-200 bg-gray-200 dark:bg-black dark:border-white/10">
|
|
25
|
+
<div class="flex w-full items-center justify-between px-8 py-4">
|
|
26
|
+
<button class="cursor-pointer select-none text-left" on:click={goHome} aria-label="Go to home page">
|
|
27
|
+
<span class="block text-xs big-title">Resource Mapper</span>
|
|
28
|
+
<span class="block text-2xl font-semibold text-gray-900 dark:text-gray-100">Architecture Atlas</span>
|
|
29
|
+
</button>
|
|
30
|
+
<div class="flex-1 flex items-center justify-center big-title text-xl">
|
|
31
|
+
{#if $selectedGroup}
|
|
32
|
+
{$selectedGroup.name}
|
|
33
|
+
{:else}
|
|
34
|
+
Overview
|
|
35
|
+
{/if}
|
|
36
|
+
</div>
|
|
37
|
+
<nav class="flex items-center gap-4">
|
|
38
|
+
<span class="text-gray-700 dark:text-gray-200" on:click={toggleLegend} role="button" tabindex="0" on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && toggleLegend()} title={$showLegend ? 'Hide Legend' : 'Show Legend'}>
|
|
39
|
+
{#if $showLegend}
|
|
40
|
+
<Icon icon="mdi:eye-off-outline" width="24" height="24" />
|
|
41
|
+
{:else}
|
|
42
|
+
<Icon icon="mdi:eye-outline" width="24" height="24" />
|
|
43
|
+
{/if}
|
|
44
|
+
</span>
|
|
45
|
+
<span class="text-gray-700 dark:text-gray-200" on:click={toggleDarkMode} role="button" tabindex="0" on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && toggleDarkMode()} title={theme.isDark ? 'Light Mode' : 'Dark Mode'}>
|
|
46
|
+
{#if theme.isDark}
|
|
47
|
+
<Icon icon="mdi:weather-sunny" width="24" height="24" />
|
|
48
|
+
{:else}
|
|
49
|
+
<Icon icon="mdi:weather-night" width="24" height="24" />
|
|
50
|
+
{/if}
|
|
51
|
+
</span>
|
|
52
|
+
<span class="text-gray-700 dark:text-gray-200" on:click={logDiagram} role="button" tabindex="0" on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && logDiagram()} title="Log Diagram">
|
|
53
|
+
<Icon icon="mdi:bug-outline" width="24" height="24" />
|
|
54
|
+
</span>
|
|
55
|
+
</nav>
|
|
56
|
+
</div>
|
|
57
|
+
</header>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<div class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-4 text-sm text-gray-700 dark:text-gray-200 shadow-lg">
|
|
2
|
+
<h3 class="mb-3 text-sm font-semibold text-gray-900 dark:text-gray-300">Legend</h3>
|
|
3
|
+
<ul class="space-y-2">
|
|
4
|
+
<li class="flex items-center gap-2">
|
|
5
|
+
<span class="h-3 w-3 rounded bg-blue-500/80 dark:bg-blue-700"></span>
|
|
6
|
+
<span>Internal group</span>
|
|
7
|
+
</li>
|
|
8
|
+
<li class="flex items-center gap-2">
|
|
9
|
+
<span class="h-3 w-3 rounded bg-amber-400/80 dark:bg-amber-600"></span>
|
|
10
|
+
<span>External group</span>
|
|
11
|
+
</li>
|
|
12
|
+
<li class="flex items-center gap-2">
|
|
13
|
+
<span class="h-1.5 w-6 rounded bg-blue-400 dark:bg-blue-600"></span>
|
|
14
|
+
<span>Internal connection</span>
|
|
15
|
+
</li>
|
|
16
|
+
<li class="flex items-center gap-2">
|
|
17
|
+
<span class="h-1.5 w-6 rounded bg-green-400 dark:bg-green-600"></span>
|
|
18
|
+
<span>Incoming connection</span>
|
|
19
|
+
</li>
|
|
20
|
+
<li class="flex items-center gap-2">
|
|
21
|
+
<span class="h-1.5 w-6 rounded bg-sky-400 dark:bg-sky-600"></span>
|
|
22
|
+
<span>Outgoing connection</span>
|
|
23
|
+
</li>
|
|
24
|
+
</ul>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { fade } from 'svelte/transition';
|
|
3
|
+
|
|
4
|
+
let { message = 'Loading...', progress = null } = $props<{ message?: string, progress?: number | null }>()
|
|
5
|
+
|
|
6
|
+
// Auto-progress simulation for indeterminate state
|
|
7
|
+
let simulatedProgress = $state(0);
|
|
8
|
+
$effect(() => {
|
|
9
|
+
if (progress !== null) return;
|
|
10
|
+
const interval = setInterval(() => {
|
|
11
|
+
simulatedProgress = (simulatedProgress + 1) % 100;
|
|
12
|
+
}, 30);
|
|
13
|
+
return () => clearInterval(interval);
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<div class="fixed inset-0 z-[100] flex flex-col items-center justify-center bg-white/40 dark:bg-slate-900/60 backdrop-blur-md" transition:fade={{ duration: 250 }}>
|
|
18
|
+
<!-- Top progress bar -->
|
|
19
|
+
<div class="fixed top-0 left-0 right-0 h-1 bg-gray-200/20 dark:bg-white/5 overflow-hidden">
|
|
20
|
+
<div
|
|
21
|
+
class="h-full bg-blue-500 shadow-[0_0_8px_rgba(59,130,246,0.5)] transition-all duration-300 ease-out"
|
|
22
|
+
style="width: {progress !== null ? progress : simulatedProgress}%"
|
|
23
|
+
></div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="flex flex-col items-center">
|
|
27
|
+
<div class="h-16 w-16 animate-spin rounded-full border-4 border-gray-200/50 border-t-blue-500 dark:border-white/10 dark:border-t-blue-400"></div>
|
|
28
|
+
<p class="mt-6 text-lg font-medium text-gray-800 dark:text-gray-100 tracking-wide">{message}</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
@keyframes progress-indeterminate {
|
|
34
|
+
0% { transform: translateX(-100%) scaleX(0.2); }
|
|
35
|
+
50% { transform: translateX(0) scaleX(0.5); }
|
|
36
|
+
100% { transform: translateX(100%) scaleX(0.2); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.animate-progress-indeterminate {
|
|
40
|
+
animation: progress-indeterminate 1.5s infinite linear;
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let { message = 'Loading...' } = $props<{ message?: string }>()
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="flex h-full w-full items-center justify-center p-8 text-center">
|
|
6
|
+
<div>
|
|
7
|
+
<div class="mx-auto h-16 w-16 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500 dark:border-gray-700 dark:border-t-blue-400"></div>
|
|
8
|
+
<p class="mt-4 text-base text-gray-500 dark:text-gray-400">{message}</p>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|