servicemap 0.1.45 → 0.1.48
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/SKILL.md +7 -4
- package/assets/desktop-icons/128x128.png +0 -0
- package/assets/desktop-icons/128x128@2x.png +0 -0
- package/assets/desktop-icons/32x32.png +0 -0
- package/assets/desktop-icons/LICENSE.lucide +15 -0
- package/assets/desktop-icons/README.md +16 -0
- package/assets/desktop-icons/icon.icns +0 -0
- package/assets/desktop-icons/icon.ico +0 -0
- package/assets/desktop-icons/icon.png +0 -0
- package/assets/desktop-icons/service-map.svg +11 -0
- package/package.json +1 -1
- package/references/desktop-fallback.md +6 -0
- package/scripts/generate-desktop-app.mjs +8 -0
- package/scripts/generate-page.mjs +17 -10
package/SKILL.md
CHANGED
|
@@ -34,10 +34,13 @@ If no website can be found, create a desktop app fallback at `servicemap-desktop
|
|
|
34
34
|
Do not deploy unless the user explicitly says yes.
|
|
35
35
|
If they approve, use the project's existing deployment path, preferring `npm run deploy` when present, then provider-specific project configuration such as Vercel or Netlify.
|
|
36
36
|
11. Report the page or desktop app path, service count, validation run, any services marked low or medium confidence, whether requested email access was enforced, and whether deployment was skipped or completed.
|
|
37
|
-
12. If a desktop app was created
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
12. If a desktop app was created, launch the native desktop app after validation instead of relying on chat follow-up buttons.
|
|
38
|
+
Open a terminal in the generated app directory and run `npm run tauri dev`.
|
|
39
|
+
Keep that process running.
|
|
40
|
+
Report whether the native desktop app opened.
|
|
41
|
+
If Tauri, Rust, platform prerequisites, or another local issue prevents the native app from launching, report the exact failure and include this fallback command:
|
|
42
|
+
`cd <absolute-desktop-app-path> && npm run tauri dev`.
|
|
43
|
+
Do not claim the app is open unless the launch succeeds.
|
|
41
44
|
|
|
42
45
|
## Safety Rules
|
|
43
46
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2022.
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Service Map desktop icon
|
|
2
|
+
|
|
3
|
+
This icon reproduces the Service Map header in the SettleAI website source, rather than using a screenshot or the scanned project's logo.
|
|
4
|
+
|
|
5
|
+
Source: `app/internal/service-map/desktop/App.tsx` (`Network size={18}`) and `app/internal/service-map/desktop/styles.css` (`.brand__mark`) in the SettleAI website repository.
|
|
6
|
+
The SVG preserves the network paths, green gradient, mint stroke, rounded tile, and relative symbol size, with transparent padding for the Dock.
|
|
7
|
+
The Network paths come from Lucide and their license is included in `LICENSE.lucide`.
|
|
8
|
+
|
|
9
|
+
Regenerate the native assets using the Tauri CLI:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
tauri icon assets/desktop-icons/service-map.svg --output assets/desktop-icons
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Keep only the desktop PNG, ICNS, and ICO files used by `scripts/generate-desktop-app.mjs`; the command also generates unused mobile and Windows Store assets.
|
|
16
|
+
The generator copies these prebuilt files so consumers do not need image-conversion tools.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 48 48">
|
|
2
|
+
<defs><linearGradient id="tile" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#245b45"/><stop offset="1" stop-color="#132e24"/></linearGradient></defs>
|
|
3
|
+
<rect x="4" y="4" width="40" height="40" rx="12.22" fill="url(#tile)" stroke="#9df0cb" stroke-opacity=".2" stroke-width="1.11"/>
|
|
4
|
+
<g transform="translate(14 14) scale(.8333333333)" fill="none" stroke="#a9f3d2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
5
|
+
<rect x="16" y="16" width="6" height="6" rx="1"/>
|
|
6
|
+
<rect x="2" y="16" width="6" height="6" rx="1"/>
|
|
7
|
+
<rect x="9" y="2" width="6" height="6" rx="1"/>
|
|
8
|
+
<path d="M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3"/>
|
|
9
|
+
<path d="M12 12V8"/>
|
|
10
|
+
</g>
|
|
11
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -27,3 +27,9 @@ npm run build
|
|
|
27
27
|
|
|
28
28
|
Do not install global tooling automatically.
|
|
29
29
|
If native packaging fails because Rust, platform SDKs, or Tauri prerequisites are missing, report that the app source was generated and the web build passed.
|
|
30
|
+
|
|
31
|
+
## Desktop identity
|
|
32
|
+
|
|
33
|
+
The generated desktop app uses the shared green Service Map network logo for its Dock and application icons.
|
|
34
|
+
The project logo belongs inside the map and must not replace `src-tauri/icons` or the configured bundle icons.
|
|
35
|
+
The packaged icon source is `assets/desktop-icons/service-map.svg`, derived from the SettleAI website Service Map header.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const desktopIcons = fileURLToPath(new URL('../assets/desktop-icons/', import.meta.url));
|
|
7
|
+
const iconFiles = ['32x32.png', '128x128.png', '128x128@2x.png', 'icon.png', 'icon.icns', 'icon.ico'];
|
|
4
8
|
import { interactiveTsxPage, prepareProjectLogo } from './generate-page.mjs';
|
|
5
9
|
|
|
6
10
|
const defaultInput = '.servicemap/internal-servicemap.json';
|
|
@@ -116,6 +120,7 @@ function tauriConfig(graph) {
|
|
|
116
120
|
bundle: {
|
|
117
121
|
active: true,
|
|
118
122
|
targets: 'all',
|
|
123
|
+
icon: iconFiles.map((name) => `icons/${name}`),
|
|
119
124
|
},
|
|
120
125
|
}, null, 2)}\n`;
|
|
121
126
|
}
|
|
@@ -170,6 +175,9 @@ async function main() {
|
|
|
170
175
|
|
|
171
176
|
await mkdir(options.output, { recursive: true });
|
|
172
177
|
await prepareProjectLogo(graph, options.output);
|
|
178
|
+
for (const name of iconFiles) {
|
|
179
|
+
await writeProjectFile(options.output, `src-tauri/icons/${name}`, await readFile(join(desktopIcons, name)));
|
|
180
|
+
}
|
|
173
181
|
await writeProjectFile(options.output, 'package.json', packageJson(graph));
|
|
174
182
|
await writeProjectFile(options.output, 'index.html', indexHtml());
|
|
175
183
|
await writeProjectFile(options.output, 'vite.config.js', viteConfig());
|
|
@@ -290,7 +290,7 @@ export function interactiveTsxPage(graph, { desktop = false } = {}) {
|
|
|
290
290
|
return `'use client';
|
|
291
291
|
|
|
292
292
|
${desktop ? "import ForceGraph3D from 'react-force-graph-3d';" : "import dynamic from 'next/dynamic';"}
|
|
293
|
-
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties, type PointerEvent } from 'react';
|
|
293
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, type CSSProperties, type PointerEvent } from 'react';
|
|
294
294
|
import type { ForceGraphMethods, NodeObject } from 'react-force-graph-3d';
|
|
295
295
|
import * as THREE from 'three';
|
|
296
296
|
import SpriteText from 'three-spritetext';
|
|
@@ -689,11 +689,13 @@ function ServiceGraph3D({
|
|
|
689
689
|
nodes,
|
|
690
690
|
selectedId,
|
|
691
691
|
resetKey,
|
|
692
|
+
controlsRef,
|
|
692
693
|
onSelect,
|
|
693
694
|
}: {
|
|
694
695
|
nodes: ServiceNode[];
|
|
695
696
|
selectedId: string | null;
|
|
696
697
|
resetKey: number;
|
|
698
|
+
controlsRef: { current: { zoom: (factor: number) => void } | null };
|
|
697
699
|
onSelect: (id: string | null) => void;
|
|
698
700
|
}) {
|
|
699
701
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -807,6 +809,8 @@ function ServiceGraph3D({
|
|
|
807
809
|
graphInstance.cameraPosition(nextPosition, target, 240);
|
|
808
810
|
}, []);
|
|
809
811
|
|
|
812
|
+
useImperativeHandle(controlsRef, () => ({ zoom }), [zoom]);
|
|
813
|
+
|
|
810
814
|
return (
|
|
811
815
|
<div className="graph-3d" ref={containerRef}>
|
|
812
816
|
<ForceGraph3D
|
|
@@ -861,10 +865,6 @@ function ServiceGraph3D({
|
|
|
861
865
|
graphRef.current?.zoomToFit(700, 130);
|
|
862
866
|
}}
|
|
863
867
|
/>
|
|
864
|
-
<div className="graph-3d__zoom" aria-label="3D zoom controls">
|
|
865
|
-
<button type="button" onClick={() => zoom(1 / 1.5)} title="Zoom in" aria-label="Zoom in">+</button>
|
|
866
|
-
<button type="button" onClick={() => zoom(1.5)} title="Zoom out" aria-label="Zoom out">−</button>
|
|
867
|
-
</div>
|
|
868
868
|
<div className="graph-3d__help" aria-hidden="true">Drag to rotate <span /> Scroll to zoom <span /> Click a service</div>
|
|
869
869
|
{selectedId && <div className="graph-3d__focus" aria-hidden="true">{activeTreeLinkIds.size} connections highlighted</div>}
|
|
870
870
|
</div>
|
|
@@ -901,6 +901,7 @@ export default function InternalServiceMapPage() {
|
|
|
901
901
|
const [panStart, setPanStart] = useState<{ pointerId: number; x: number; y: number; panX: number; panY: number } | null>(null);
|
|
902
902
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
903
903
|
const [reset3DKey, setReset3DKey] = useState(0);
|
|
904
|
+
const controls3DRef = useRef<{ zoom: (factor: number) => void } | null>(null);
|
|
904
905
|
const startNodeDrag = (event: PointerEvent<HTMLButtonElement>, node: ServiceNode) => {
|
|
905
906
|
if (event.button !== 0) return;
|
|
906
907
|
event.stopPropagation();
|
|
@@ -946,8 +947,14 @@ export default function InternalServiceMapPage() {
|
|
|
946
947
|
const selected = nodes.find((service) => service.id === selectedId) ?? null;
|
|
947
948
|
const descendantIds = useMemo(() => collectDescendantIds(nodes, selectedId), [nodes, selectedId]);
|
|
948
949
|
const highlightedChildIds = useMemo(() => selectedId === 'project' ? descendantIds : new Set<string>(), [descendantIds, selectedId]);
|
|
949
|
-
const zoomIn = () =>
|
|
950
|
-
|
|
950
|
+
const zoomIn = () => {
|
|
951
|
+
if (mode === '3d') controls3DRef.current?.zoom(1 / 1.5);
|
|
952
|
+
else setZoom((current) => Math.min(1.45, current * 1.5));
|
|
953
|
+
};
|
|
954
|
+
const zoomOut = () => {
|
|
955
|
+
if (mode === '3d') controls3DRef.current?.zoom(1.5);
|
|
956
|
+
else setZoom((current) => current / 1.5);
|
|
957
|
+
};
|
|
951
958
|
const resetZoom = () => {
|
|
952
959
|
setZoom(1);
|
|
953
960
|
setPan({ x: 0, y: 0 });
|
|
@@ -1051,7 +1058,7 @@ export default function InternalServiceMapPage() {
|
|
|
1051
1058
|
<>
|
|
1052
1059
|
<div className="map-plane" style={{ '--zoom': zoom, '--pan-x': \`\${pan.x}px\`, '--pan-y': \`\${pan.y}px\` } as CSSProperties}>
|
|
1053
1060
|
<Connections nodes={nodes} selectedId={selectedId} />
|
|
1054
|
-
<button type="button" className={\`hub \${selectedId === 'project' ? 'is-selected' : ''}\`} onClick={() => setSelectedId('project')} aria-label={\`Trace \${graph.project?.name ?? 'Project'} child services\`}><div className="hub-inner"><ProjectMark /></div></button>
|
|
1061
|
+
<button type="button" className={\`hub \${selectedId === 'project' ? 'is-selected' : ''}\`} onClick={() => setSelectedId((current) => current === 'project' ? null : 'project')} aria-label={selectedId === 'project' ? \`Stop tracing \${graph.project?.name ?? 'Project'} child services\` : \`Trace \${graph.project?.name ?? 'Project'} child services\`} aria-pressed={selectedId === 'project'}><div className="hub-inner"><ProjectMark /></div></button>
|
|
1055
1062
|
{nodes.map((service) => {
|
|
1056
1063
|
const isHighlightedChild = highlightedChildIds.has(service.id);
|
|
1057
1064
|
const isRelated = descendantIds.has(service.id) || selected?.parentId === service.id || isHighlightedChild;
|
|
@@ -1088,12 +1095,12 @@ export default function InternalServiceMapPage() {
|
|
|
1088
1095
|
</div>
|
|
1089
1096
|
</>
|
|
1090
1097
|
) : (
|
|
1091
|
-
<ServiceGraph3D nodes={nodes} selectedId={selectedId} resetKey={reset3DKey} onSelect={setSelectedId} />
|
|
1098
|
+
<ServiceGraph3D nodes={nodes} selectedId={selectedId} resetKey={reset3DKey} controlsRef={controls3DRef} onSelect={setSelectedId} />
|
|
1092
1099
|
)}
|
|
1093
1100
|
<div className="zoom-controls" aria-label="Map zoom controls">
|
|
1094
1101
|
<button type="button" className="zoom-button" onClick={zoomIn} aria-label="Zoom in">+</button>
|
|
1095
1102
|
<button type="button" className="zoom-button" onClick={zoomOut} aria-label="Zoom out">−</button>
|
|
1096
|
-
<button type="button" className="zoom-button" onClick={
|
|
1103
|
+
<button type="button" className="zoom-button" onClick={centerMap} aria-label="Reset zoom"><span className="fit-icon" aria-hidden="true" /></button>
|
|
1097
1104
|
</div>
|
|
1098
1105
|
<footer className="scan-foot">
|
|
1099
1106
|
<div>Generated {new Date(graph.generatedAt).toLocaleString()}</div>
|