@ibgib/space-gib 0.0.34 → 0.0.36

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.
Files changed (54) hide show
  1. package/dist/client/bootstrap.mjs +1 -1
  2. package/dist/client/{chunk-25PXC4HP.mjs → chunk-5DTAUY3E.mjs} +1586 -464
  3. package/dist/client/chunk-J7X3XTAI.mjs +53 -0
  4. package/dist/client/css/activity-bar.css +21 -3
  5. package/dist/client/css/layout.css +126 -5
  6. package/dist/client/css/tutorial.css +327 -0
  7. package/dist/client/index.html +6 -0
  8. package/dist/client/index.mjs +22 -45
  9. package/dist/client/privacy.html +22 -22
  10. package/dist/client/script.mjs +1 -1
  11. package/dist/client/style.css +1 -0
  12. package/dist/server/server.mjs +3 -3
  13. package/package.json +6 -6
  14. package/src/client/AUTO-GENERATED-version.mts +1 -1
  15. package/src/client/cosmos/seed-cosmos-workspace.mts +20 -6
  16. package/src/client/css/activity-bar.css +21 -3
  17. package/src/client/css/layout.css +126 -5
  18. package/src/client/css/tutorial.css +327 -0
  19. package/src/client/dev-tools/vcs-workspace.mts +4 -4
  20. package/src/client/index.html +6 -0
  21. package/src/client/privacy.html +22 -22
  22. package/src/client/style.css +1 -0
  23. package/src/client/ui/component/changes/changes.css +413 -0
  24. package/src/client/ui/component/changes/changes.html +37 -0
  25. package/src/client/ui/component/changes/changes.mts +766 -0
  26. package/src/client/ui/component/changes/index.mts +9 -0
  27. package/src/client/ui/component/chat-view/chat-view.html +6 -58
  28. package/src/client/ui/component/chat-view/chat-view.mts +13 -26
  29. package/src/client/ui/component/clone/clone.mts +67 -39
  30. package/src/client/ui/component/code-editor/code-editor.css +91 -9
  31. package/src/client/ui/component/code-editor/code-editor.html +58 -45
  32. package/src/client/ui/component/code-editor/code-editor.mts +265 -49
  33. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.css +1 -1
  34. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.html +5 -10
  35. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.mts +328 -64
  36. package/src/client/ui/component/file-explorer/file-explorer.css +475 -11
  37. package/src/client/ui/component/file-explorer/file-explorer.html +45 -65
  38. package/src/client/ui/component/file-explorer/file-explorer.mts +1164 -98
  39. package/src/client/ui/component/nested-chat/chat/chat.mts +5 -90
  40. package/src/client/ui/component/nested-chat/nested-chat.mts +10 -8
  41. package/src/client/ui/component/timeline/timeline.css +16 -1
  42. package/src/client/ui/component/timeline/timeline.mts +37 -93
  43. package/src/client/ui/component/tutorial/index.mts +9 -0
  44. package/src/client/ui/component/tutorial/scripts/pitch-tour-script.mts +265 -0
  45. package/src/client/ui/component/tutorial/tutorial-controller.mts +622 -0
  46. package/src/client/ui/component/tutorial/tutorial-types.mts +45 -0
  47. package/src/client/ui/component/tutorial/tutorial.css +260 -0
  48. package/src/client/ui/router/space-gib-router-helpers.mts +33 -233
  49. package/src/client/ui/router/space-gib-router-helpers.respec.mts +298 -122
  50. package/src/client/ui/router/space-gib-router-service.mts +28 -151
  51. package/src/client/ui/router/space-gib-router-types.mts +32 -76
  52. package/src/client/ui/shell/cosmic-big-bang.mts +12 -3
  53. package/src/client/ui/shell/space-gib-shell-service.mts +715 -143
  54. package/dist/client/chunk-RXWLL2AI.mjs +0 -30
@@ -0,0 +1,260 @@
1
+ /**
2
+ * tutorial.css — Styles for the Space-Gib Interactive Tutorial & Spotlight Tour
3
+ */
4
+
5
+ .tutorial-overlay-container {
6
+ position: fixed;
7
+ inset: 0;
8
+ z-index: 10000;
9
+ pointer-events: none;
10
+ transition: opacity 0.3s ease;
11
+ }
12
+
13
+ /* Semi-transparent dimmer backdrop */
14
+ .tutorial-backdrop {
15
+ position: absolute;
16
+ inset: 0;
17
+ background: rgba(8, 10, 15, 0.75);
18
+ backdrop-filter: blur(3px);
19
+ -webkit-backdrop-filter: blur(3px);
20
+ pointer-events: auto;
21
+ transition: clip-path 0.35s cubic-bezier(0.2, 0.9, 0.3, 1);
22
+ }
23
+
24
+ /* Glowing spotlight ring around the active target */
25
+ .tutorial-spotlight-ring {
26
+ position: absolute;
27
+ border-radius: 8px;
28
+ box-shadow: 0 0 0 4px rgba(255, 204, 0, 0.6),
29
+ 0 0 25px 6px rgba(255, 180, 0, 0.4),
30
+ inset 0 0 15px rgba(255, 204, 0, 0.2);
31
+ pointer-events: none;
32
+ transition: top 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
33
+ left 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
34
+ width 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
35
+ height 0.35s cubic-bezier(0.2, 0.9, 0.3, 1);
36
+ z-index: 10001;
37
+ }
38
+
39
+ /* Movable Golden Beacon Button */
40
+ .tutorial-beacon {
41
+ position: absolute;
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 8px;
45
+ padding: 8px 18px;
46
+ background: linear-gradient(135deg, #ffd700 0%, #ff9900 100%);
47
+ color: #121212;
48
+ font-family: inherit;
49
+ font-size: 0.95rem;
50
+ font-weight: 700;
51
+ border: 2px solid rgba(255, 255, 255, 0.8);
52
+ border-radius: 9999px;
53
+ box-shadow: 0 4px 20px rgba(255, 180, 0, 0.65), 0 0 30px rgba(255, 215, 0, 0.4);
54
+ cursor: pointer;
55
+ pointer-events: auto;
56
+ z-index: 10002;
57
+ transition: top 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
58
+ left 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
59
+ transform 0.2s ease,
60
+ box-shadow 0.2s ease;
61
+ animation: beacon-pulse 2s infinite ease-in-out;
62
+ }
63
+
64
+ .tutorial-beacon:hover {
65
+ transform: scale(1.06);
66
+ box-shadow: 0 6px 28px rgba(255, 190, 0, 0.85), 0 0 40px rgba(255, 230, 100, 0.6);
67
+ }
68
+
69
+ .tutorial-beacon:active {
70
+ transform: scale(0.96);
71
+ }
72
+
73
+ .tutorial-beacon-icon {
74
+ font-size: 1.15rem;
75
+ line-height: 1;
76
+ }
77
+
78
+ .tutorial-beacon-text {
79
+ letter-spacing: 0.02em;
80
+ }
81
+
82
+ @keyframes beacon-pulse {
83
+ 0%, 100% {
84
+ box-shadow: 0 4px 20px rgba(255, 180, 0, 0.65), 0 0 15px rgba(255, 215, 0, 0.4);
85
+ }
86
+ 50% {
87
+ box-shadow: 0 4px 30px rgba(255, 200, 0, 0.9), 0 0 35px rgba(255, 230, 50, 0.7);
88
+ }
89
+ }
90
+
91
+ /* Floating Glassmorphic Narration Card */
92
+ .tutorial-card {
93
+ position: absolute;
94
+ width: 440px;
95
+ max-width: calc(100vw - 32px);
96
+ background: rgba(18, 22, 34, 0.88);
97
+ backdrop-filter: blur(16px);
98
+ -webkit-backdrop-filter: blur(16px);
99
+ border: 1px solid rgba(255, 215, 0, 0.25);
100
+ border-radius: 14px;
101
+ box-shadow: 0 16px 40px rgba(0, 0, 0, 0.6), 0 0 20px rgba(255, 200, 0, 0.15);
102
+ padding: 22px 24px;
103
+ color: #e2e8f0;
104
+ pointer-events: auto;
105
+ z-index: 10002;
106
+ transition: top 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
107
+ left 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
108
+ opacity 0.25s ease;
109
+ }
110
+
111
+ .tutorial-card.placement-center {
112
+ top: 50% !important;
113
+ left: 50% !important;
114
+ transform: translate(-50%, -50%);
115
+ width: 580px;
116
+ border-color: rgba(255, 215, 0, 0.45);
117
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8), 0 0 40px rgba(255, 200, 0, 0.25);
118
+ }
119
+
120
+ .tutorial-card-header {
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: space-between;
124
+ margin-bottom: 12px;
125
+ }
126
+
127
+ .tutorial-step-badge {
128
+ font-size: 0.75rem;
129
+ font-weight: 700;
130
+ text-transform: uppercase;
131
+ letter-spacing: 0.08em;
132
+ color: #ffd700;
133
+ background: rgba(255, 215, 0, 0.12);
134
+ padding: 4px 10px;
135
+ border-radius: 9999px;
136
+ border: 1px solid rgba(255, 215, 0, 0.3);
137
+ }
138
+
139
+ .tutorial-card-close {
140
+ background: none;
141
+ border: none;
142
+ color: #94a3b8;
143
+ font-size: 1.25rem;
144
+ cursor: pointer;
145
+ padding: 4px 8px;
146
+ border-radius: 6px;
147
+ transition: color 0.15s ease, background 0.15s ease;
148
+ }
149
+
150
+ .tutorial-card-close:hover {
151
+ color: #ffffff;
152
+ background: rgba(255, 255, 255, 0.1);
153
+ }
154
+
155
+ .tutorial-card-title {
156
+ margin: 0 0 12px 0;
157
+ font-size: 1.2rem;
158
+ font-weight: 700;
159
+ color: #ffffff;
160
+ line-height: 1.35;
161
+ }
162
+
163
+ .tutorial-card-body {
164
+ font-size: 0.92rem;
165
+ line-height: 1.6;
166
+ color: #cbd5e1;
167
+ margin-bottom: 20px;
168
+ max-height: 55vh;
169
+ overflow-y: auto;
170
+ }
171
+
172
+ .tutorial-card-body p {
173
+ margin: 0 0 10px 0;
174
+ }
175
+
176
+ .tutorial-card-body p:last-child {
177
+ margin-bottom: 0;
178
+ }
179
+
180
+ .tutorial-card-body strong {
181
+ color: #f8fafc;
182
+ }
183
+
184
+ .tutorial-card-body em {
185
+ color: #ffd700;
186
+ font-style: normal;
187
+ }
188
+
189
+ .tutorial-card-footer {
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: space-between;
193
+ padding-top: 14px;
194
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
195
+ }
196
+
197
+ .tutorial-progress-dots {
198
+ display: flex;
199
+ gap: 6px;
200
+ }
201
+
202
+ .tutorial-dot {
203
+ width: 8px;
204
+ height: 8px;
205
+ border-radius: 9999px;
206
+ background: rgba(255, 255, 255, 0.2);
207
+ transition: background 0.25s ease, transform 0.25s ease;
208
+ }
209
+
210
+ .tutorial-dot.active {
211
+ background: #ffd700;
212
+ transform: scale(1.25);
213
+ box-shadow: 0 0 8px #ffd700;
214
+ }
215
+
216
+ .tutorial-card-actions {
217
+ display: flex;
218
+ align-items: center;
219
+ gap: 10px;
220
+ }
221
+
222
+ .tutorial-btn-skip {
223
+ background: none;
224
+ border: none;
225
+ color: #94a3b8;
226
+ font-size: 0.85rem;
227
+ cursor: pointer;
228
+ padding: 6px 12px;
229
+ border-radius: 6px;
230
+ transition: color 0.15s ease;
231
+ }
232
+
233
+ .tutorial-btn-skip:hover {
234
+ color: #ffffff;
235
+ }
236
+
237
+ .tutorial-btn-next {
238
+ display: inline-flex;
239
+ align-items: center;
240
+ gap: 6px;
241
+ background: linear-gradient(135deg, #ffd700 0%, #ffaa00 100%);
242
+ color: #0f172a;
243
+ font-weight: 700;
244
+ font-size: 0.9rem;
245
+ padding: 8px 18px;
246
+ border: none;
247
+ border-radius: 8px;
248
+ cursor: pointer;
249
+ box-shadow: 0 4px 14px rgba(255, 180, 0, 0.4);
250
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
251
+ }
252
+
253
+ .tutorial-btn-next:hover {
254
+ transform: translateY(-1px);
255
+ box-shadow: 0 6px 20px rgba(255, 180, 0, 0.6);
256
+ }
257
+
258
+ .tutorial-btn-next:active {
259
+ transform: translateY(1px);
260
+ }
@@ -2,199 +2,50 @@
2
2
  * @module client/ui/router/space-gib-router-helpers
3
3
  *
4
4
  * Route parsing and formatting helpers for the Space-Gib Client Router.
5
- * Pure isomorphic functions with zero DOM dependency.
5
+ * Delegates universal Cosmos & Galaxy routes to @ibgib/web-gib,
6
+ * while handling space-gib specific routes (/admin/review).
6
7
  */
7
8
 
8
9
  import {
9
- SpaceGibRouteInfo, SpaceGibTenant,
10
+ parseCosmosRoute,
11
+ formatCosmosRoute,
12
+ isSameCosmosRoute,
13
+ safeDecode,
14
+ ParseCosmosRouteOptions,
15
+ CosmosRouteInfo,
16
+ } from '@ibgib/web-gib/dist/ui/router/index.mjs';
17
+
18
+ import {
19
+ SpaceGibRouteInfo,
20
+ AdminReviewRouteInfo,
10
21
  } from './space-gib-router-types.mjs';
11
22
 
12
- /**
13
- * Safely decodes a URI component, returning the raw string if decoding fails.
14
- */
15
- function safeDecode(str: string): string {
16
- try {
17
- return decodeURIComponent(str);
18
- } catch {
19
- return str;
20
- }
21
- }
23
+ export { safeDecode };
24
+ export type ParseSpaceGibRouteOptions = ParseCosmosRouteOptions;
22
25
 
23
26
  /**
24
27
  * Parses a browser pathname into a structured SpaceGibRouteInfo.
25
- *
26
- * Supports:
27
- * - Polycosmos: / or /polycosmos or /@:siteUsername or /keystone/:keystoneTjp
28
- * - Cosmos: /:cosmos or /@:siteUsername/:cosmos or /keystone/:keystoneTjp/:cosmos
29
- * - Code Galaxy: /:cosmos/code/:galaxyId[/:branch[/*filePath]]
30
- * - Notes Galaxy: /:cosmos/notes/:galaxyId[/:topicTjp]
31
- * - Raw Addr (Snapshot): /:cosmos/:spaceId/addr/:ibGibAddr
32
- * - Raw Tjp (Living Tip): /:cosmos/:spaceId/tjp/:tjpAddr
33
- * - Admin Review: /admin/review[/:tjpGib]
28
+ * Handles /admin/review explicitly, and delegates all Cosmos / Galaxy / Raw routes to @ibgib/web-gib.
34
29
  */
35
- export function parseSpaceGibRoute(pathname: string): SpaceGibRouteInfo {
30
+ export function parseSpaceGibRoute(pathname: string, opts?: ParseSpaceGibRouteOptions): SpaceGibRouteInfo {
36
31
  let p = (pathname || '').trim();
37
32
 
38
- let queryObj: Record<string, string> | undefined;
39
- const qIdx = p.indexOf('?');
40
- if (qIdx !== -1) {
41
- const qStr = p.substring(qIdx + 1);
42
- p = p.substring(0, qIdx);
43
- const searchParams = new URLSearchParams(qStr);
44
- const q: Record<string, string> = {};
45
- searchParams.forEach((val, key) => {
46
- q[key] = val;
47
- });
48
- if (Object.keys(q).length > 0) {
49
- queryObj = q;
50
- }
51
- }
52
- const hIdx = p.indexOf('#');
53
- if (hIdx !== -1) { p = p.substring(0, hIdx); }
33
+ // Check query string/hash for /admin/review prefix
34
+ let cleanPath = p;
35
+ const qIdx = cleanPath.indexOf('?');
36
+ if (qIdx !== -1) { cleanPath = cleanPath.substring(0, qIdx); }
37
+ const hIdx = cleanPath.indexOf('#');
38
+ if (hIdx !== -1) { cleanPath = cleanPath.substring(0, hIdx); }
54
39
 
55
- if (!p || p === '/') {
56
- return { kind: 'polycosmos' };
57
- }
58
-
59
- // Special Route: /admin/review
60
- if (p === '/admin/review') {
40
+ if (cleanPath === '/admin/review') {
61
41
  return { kind: 'admin-review' };
62
42
  }
63
- if (p.startsWith('/admin/review/')) {
64
- const tjpGib = safeDecode(p.slice('/admin/review/'.length)).trim();
43
+ if (cleanPath.startsWith('/admin/review/')) {
44
+ const tjpGib = safeDecode(cleanPath.slice('/admin/review/'.length)).trim();
65
45
  return { kind: 'admin-review', ...(tjpGib ? { tjpGib } : {}) };
66
46
  }
67
47
 
68
- const rawSegments = p.split('/').filter(s => s.length > 0);
69
- if (rawSegments.length === 0) {
70
- return { kind: 'polycosmos' };
71
- }
72
-
73
- // Extract tenant prefix if present (@username or /keystone/tjp)
74
- let tenant: SpaceGibTenant | undefined;
75
- let segIdx = 0;
76
-
77
- if (rawSegments[0].startsWith('@')) {
78
- const username = safeDecode(rawSegments[0].slice(1)).trim();
79
- if (username) {
80
- tenant = { type: 'username', siteUsername: username };
81
- segIdx = 1;
82
- }
83
- } else if (rawSegments[0] === 'keystone' && rawSegments.length > 1) {
84
- const keystoneTjp = safeDecode(rawSegments[1]).trim();
85
- if (keystoneTjp) {
86
- tenant = { type: 'keystone', keystoneTjp };
87
- segIdx = 2;
88
- }
89
- }
90
-
91
- const remaining = rawSegments.slice(segIdx);
92
-
93
- // If only tenant specifier was given (e.g. /@bill or /keystone/tjp123)
94
- if (remaining.length === 0) {
95
- return { kind: 'polycosmos', ...(tenant ? { tenant } : {}) };
96
- }
97
-
98
- if (remaining.length === 1 && remaining[0] === 'polycosmos') {
99
- return { kind: 'polycosmos', ...(tenant ? { tenant } : {}) };
100
- }
101
-
102
- const cosmos = safeDecode(remaining[0]).trim();
103
-
104
- if (remaining.length === 1) {
105
- return { kind: 'cosmos', ...(tenant ? { tenant } : {}), cosmos };
106
- }
107
-
108
- const second = safeDecode(remaining[1]).trim();
109
-
110
- // Code Galaxy: /:cosmos/code/:galaxyId[/:branch[/*filePath]]
111
- if (second === 'code') {
112
- const galaxyId = remaining[2] ? safeDecode(remaining[2]).trim() : '';
113
- if (!galaxyId) {
114
- return { kind: 'cosmos', ...(tenant ? { tenant } : {}), cosmos };
115
- }
116
- const branch = remaining[3] ? safeDecode(remaining[3]).trim() : undefined;
117
- const filePath = remaining.length > 4 ? remaining.slice(4).map(safeDecode).join('/') : undefined;
118
- return {
119
- kind: 'code-galaxy',
120
- ...(tenant ? { tenant } : {}),
121
- cosmos,
122
- galaxyId,
123
- ...(branch ? { branch } : {}),
124
- ...(filePath ? { filePath } : {}),
125
- };
126
- }
127
-
128
- // Notes Galaxy: /:cosmos/notes/:galaxyId[/tjp/:tjp1/:tjp2...] or [/addr/:addr1/:addr2...] or [/:topicTjp]
129
- if (second === 'notes') {
130
- const galaxyId = remaining[2] ? safeDecode(remaining[2]).trim() : '';
131
- if (!galaxyId) {
132
- return { kind: 'cosmos', ...(tenant ? { tenant } : {}), cosmos };
133
- }
134
-
135
- let trailMode: 'tjp' | 'addr' | undefined;
136
- let trail: string[] | undefined;
137
- let topicTjp: string | undefined;
138
-
139
- if (remaining.length > 3) {
140
- const modeCandidate = safeDecode(remaining[3]).trim();
141
- if (modeCandidate === 'tjp' || modeCandidate === 'addr') {
142
- trailMode = modeCandidate;
143
- const trailSegments = remaining.slice(4).map(s => safeDecode(s).trim()).filter(s => s.length > 0);
144
- if (trailSegments.length > 0) {
145
- trail = trailSegments;
146
- topicTjp = trailSegments[0];
147
- }
148
- } else {
149
- // Backward compatibility / shorthand: /notes/:galaxyId/:topicTjp
150
- topicTjp = modeCandidate;
151
- trailMode = 'tjp';
152
- trail = [modeCandidate];
153
- }
154
- }
155
-
156
- return {
157
- kind: 'notes-galaxy',
158
- ...(tenant ? { tenant } : {}),
159
- cosmos,
160
- galaxyId,
161
- ...(trailMode ? { trailMode } : {}),
162
- ...(trail ? { trail } : {}),
163
- ...(topicTjp ? { topicTjp } : {}),
164
- ...(queryObj ? { query: queryObj } : {}),
165
- };
166
- }
167
-
168
- // Mechanism 1: Space-Qualified Raw Addrs
169
- // /:cosmos/:spaceId/addr/:ibGibAddr
170
- // /:cosmos/:spaceId/tjp/:tjpAddr
171
- if (remaining.length >= 4) {
172
- const third = safeDecode(remaining[2]).trim();
173
- const spaceId = second;
174
- if (third === 'addr') {
175
- const ibGibAddr = safeDecode(remaining.slice(3).join('/')).trim();
176
- return {
177
- kind: 'raw-addr',
178
- ...(tenant ? { tenant } : {}),
179
- cosmos,
180
- spaceId,
181
- ibGibAddr,
182
- };
183
- }
184
- if (third === 'tjp') {
185
- const tjpAddr = safeDecode(remaining.slice(3).join('/')).trim();
186
- return {
187
- kind: 'raw-tjp',
188
- ...(tenant ? { tenant } : {}),
189
- cosmos,
190
- spaceId,
191
- tjpAddr,
192
- };
193
- }
194
- }
195
-
196
- // Fallback: Cosmos route
197
- return { kind: 'cosmos', ...(tenant ? { tenant } : {}), cosmos };
48
+ return parseCosmosRoute(pathname, opts) as SpaceGibRouteInfo;
198
49
  }
199
50
 
200
51
  /**
@@ -204,62 +55,7 @@ export function formatSpaceGibRoute(route: SpaceGibRouteInfo): string {
204
55
  if (route.kind === 'admin-review') {
205
56
  return `/admin/review${route.tjpGib ? `/${encodeURIComponent(route.tjpGib)}` : ''}`;
206
57
  }
207
-
208
- let prefix = '';
209
- if (route.tenant) {
210
- if (route.tenant.type === 'username') {
211
- prefix = `/@${encodeURIComponent(route.tenant.siteUsername)}`;
212
- } else if (route.tenant.type === 'keystone') {
213
- prefix = `/keystone/${encodeURIComponent(route.tenant.keystoneTjp)}`;
214
- }
215
- }
216
-
217
- switch (route.kind) {
218
- case 'polycosmos':
219
- return prefix ? `${prefix}/polycosmos` : '/';
220
-
221
- case 'cosmos':
222
- return `${prefix}/${encodeURIComponent(route.cosmos)}`;
223
-
224
- case 'code-galaxy': {
225
- let res = `${prefix}/${encodeURIComponent(route.cosmos)}/code/${encodeURIComponent(route.galaxyId)}`;
226
- if (route.branch) {
227
- res += `/${encodeURIComponent(route.branch)}`;
228
- if (route.filePath) {
229
- res += `/${route.filePath.split('/').map(encodeURIComponent).join('/')}`;
230
- }
231
- }
232
- return res;
233
- }
234
-
235
- case 'notes-galaxy': {
236
- let res = `${prefix}/${encodeURIComponent(route.cosmos)}/notes/${encodeURIComponent(route.galaxyId)}`;
237
- if (route.trail && route.trail.length > 0) {
238
- const mode = route.trailMode || 'tjp';
239
- res += `/${mode}/${route.trail.map(encodeURIComponent).join('/')}`;
240
- } else if (route.topicTjp) {
241
- res += `/${encodeURIComponent(route.topicTjp)}`;
242
- }
243
- if (route.query && Object.keys(route.query).length > 0) {
244
- const sp = new URLSearchParams();
245
- for (const [k, v] of Object.entries(route.query)) {
246
- if (v !== undefined) { sp.set(k, v); }
247
- }
248
- const qs = sp.toString();
249
- if (qs) { res += `?${qs}`; }
250
- }
251
- return res;
252
- }
253
-
254
- case 'raw-addr':
255
- return `${prefix}/${encodeURIComponent(route.cosmos)}/${encodeURIComponent(route.spaceId)}/addr/${encodeURIComponent(route.ibGibAddr)}`;
256
-
257
- case 'raw-tjp':
258
- return `${prefix}/${encodeURIComponent(route.cosmos)}/${encodeURIComponent(route.spaceId)}/tjp/${encodeURIComponent(route.tjpAddr)}`;
259
-
260
- default:
261
- return '/';
262
- }
58
+ return formatCosmosRoute(route as CosmosRouteInfo);
263
59
  }
264
60
 
265
61
  /**
@@ -268,5 +64,9 @@ export function formatSpaceGibRoute(route: SpaceGibRouteInfo): string {
268
64
  export function isSameRoute(a?: SpaceGibRouteInfo, b?: SpaceGibRouteInfo): boolean {
269
65
  if (!a && !b) { return true; }
270
66
  if (!a || !b) { return false; }
271
- return formatSpaceGibRoute(a) === formatSpaceGibRoute(b);
67
+ if (a.kind === 'admin-review' || b.kind === 'admin-review') {
68
+ if (a.kind !== b.kind) { return false; }
69
+ return (a as AdminReviewRouteInfo).tjpGib === (b as AdminReviewRouteInfo).tjpGib;
70
+ }
71
+ return isSameCosmosRoute(a as CosmosRouteInfo, b as CosmosRouteInfo);
272
72
  }