@sprig-and-prose/sprig-ui-csr 0.1.1 → 0.2.0
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/package.json +1 -2
- package/src/App.svelte +41 -8
- package/src/lib/components/GlobalSearch.svelte +45 -3
- package/src/lib/data/universeStore.js +22 -5
- package/src/lib/router.js +12 -0
- package/src/pages/ConceptPage.svelte +146 -296
- package/src/pages/ReferencePage.svelte +104 -0
- package/src/pages/SeriesPage.svelte +146 -296
- package/src/lib/references/isWildcardPath.js +0 -9
- package/src/lib/references/linkForPath.js +0 -65
- package/src/lib/references/linkForRepository.js +0 -42
|
@@ -5,9 +5,7 @@
|
|
|
5
5
|
import ContentsCard from '../lib/components/ContentsCard.svelte';
|
|
6
6
|
import FooterStatus from '../lib/components/FooterStatus.svelte';
|
|
7
7
|
import PageHeader from '../lib/components/PageHeader.svelte';
|
|
8
|
-
|
|
9
|
-
import { linkForRepository } from '../lib/references/linkForRepository.js';
|
|
10
|
-
import { isWildcardPath } from '../lib/references/isWildcardPath.js';
|
|
8
|
+
// Reference routing stays internal for series pages
|
|
11
9
|
|
|
12
10
|
/** @type {{ universe: string, series: string }} */
|
|
13
11
|
export let params;
|
|
@@ -21,7 +19,7 @@
|
|
|
21
19
|
|
|
22
20
|
/**
|
|
23
21
|
* @typedef {{ raw?:string, normalized?:string, source?:any }} TextBlock
|
|
24
|
-
* @typedef {{
|
|
22
|
+
* @typedef {{ id:string, name:string, kind?:string, title?:string, describe?:TextBlock, note?:TextBlock, urls?:string[], paths?:string[], repositoryRef?:string }} ReferenceModel
|
|
25
23
|
*/
|
|
26
24
|
|
|
27
25
|
$: subtitle = (() => {
|
|
@@ -52,133 +50,50 @@
|
|
|
52
50
|
: `A series in ${params.universe}`;
|
|
53
51
|
})();
|
|
54
52
|
|
|
53
|
+
$: referenceItems = ($currentSeries?.references || [])
|
|
54
|
+
.map((id) => $universeGraph?.references?.[id])
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
|
|
55
57
|
/**
|
|
56
|
-
*
|
|
57
|
-
* @param {
|
|
58
|
-
* @returns {Array<{path:string, describe?:TextBlock}>}
|
|
58
|
+
* @param {string} url
|
|
59
|
+
* @param {string | undefined} label
|
|
59
60
|
*/
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// Shape B: paths is array of objects
|
|
64
|
-
if (typeof ref.paths[0] === 'object' && ref.paths[0] !== null && 'path' in ref.paths[0]) {
|
|
65
|
-
return ref.paths.map((/** @type {{path:string, describe?:TextBlock}} */ p) => ({
|
|
66
|
-
path: p.path,
|
|
67
|
-
describe: p.describe,
|
|
68
|
-
}));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Shape A: paths is array of strings
|
|
72
|
-
return ref.paths.map((/** @type {string} */ p) => ({
|
|
73
|
-
path: p,
|
|
74
|
-
describe: undefined, // No per-path describe in Shape A
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Group references by repository, preserving source order
|
|
79
|
-
$: groupedReferences = (() => {
|
|
80
|
-
const refs = $currentSeries?.references || [];
|
|
81
|
-
if (refs.length === 0) return [];
|
|
82
|
-
|
|
83
|
-
const groups = new Map();
|
|
84
|
-
const order = [];
|
|
85
|
-
|
|
86
|
-
for (const ref of refs) {
|
|
87
|
-
const repo = ref.repository;
|
|
88
|
-
if (!groups.has(repo)) {
|
|
89
|
-
groups.set(repo, {
|
|
90
|
-
referenceGroups: [],
|
|
91
|
-
});
|
|
92
|
-
order.push(repo);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const group = groups.get(repo);
|
|
96
|
-
const normalizedPaths = normalizePaths(ref);
|
|
97
|
-
const pathEntries = normalizedPaths.map((pathEntry) => ({
|
|
98
|
-
path: pathEntry.path,
|
|
99
|
-
perPathDescribe: pathEntry.describe,
|
|
100
|
-
}));
|
|
101
|
-
|
|
102
|
-
group.referenceGroups.push({
|
|
103
|
-
paths: pathEntries,
|
|
104
|
-
groupDescribe: ref.describe?.normalized || undefined,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return order.map((repo) => ({
|
|
109
|
-
repository: repo,
|
|
110
|
-
referenceGroups: groups.get(repo).referenceGroups,
|
|
111
|
-
}));
|
|
112
|
-
})();
|
|
61
|
+
function makeLinkLabel(url, label) {
|
|
62
|
+
return label && label.trim().length > 0 ? label : url;
|
|
63
|
+
}
|
|
113
64
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
65
|
+
/**
|
|
66
|
+
* @param {ReferenceModel} ref
|
|
67
|
+
*/
|
|
68
|
+
function getRepositoryForReference(ref) {
|
|
69
|
+
if (!ref.repositoryRef || !$universeGraph?.repositories) return null;
|
|
70
|
+
return $universeGraph.repositories[ref.repositoryRef] || null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
$: referenceGroups = (() => {
|
|
74
|
+
/** @type {Array<{ repo: any | null, refs: ReferenceModel[] }>} */
|
|
75
|
+
const groups = [];
|
|
76
|
+
const byRepo = new Map();
|
|
77
|
+
|
|
78
|
+
for (const ref of referenceItems) {
|
|
79
|
+
if (!ref) continue;
|
|
80
|
+
if (ref.repositoryRef) {
|
|
81
|
+
let group = byRepo.get(ref.repositoryRef);
|
|
82
|
+
if (!group) {
|
|
83
|
+
const repo = getRepositoryForReference(ref);
|
|
84
|
+
group = { repo, refs: [] };
|
|
85
|
+
byRepo.set(ref.repositoryRef, group);
|
|
86
|
+
groups.push(group);
|
|
129
87
|
}
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
return urls;
|
|
133
|
-
})();
|
|
134
|
-
|
|
135
|
-
// Helper function to generate path URLs reactively
|
|
136
|
-
function getPathUrl(repository, path) {
|
|
137
|
-
const graph = $universeGraph;
|
|
138
|
-
if (!graph?.repositories) return null;
|
|
139
|
-
const repoConfig = graph.repositories[repository];
|
|
140
|
-
if (!repoConfig) return null;
|
|
141
|
-
const { kind, options } = repoConfig;
|
|
142
|
-
const defaultBranch = options?.defaultBranch || 'main';
|
|
143
|
-
|
|
144
|
-
if (kind === 'sprig-repository-github') {
|
|
145
|
-
let baseUrl;
|
|
146
|
-
if (options?.url) {
|
|
147
|
-
baseUrl = options.url;
|
|
148
|
-
} else if (options?.owner && options?.repo) {
|
|
149
|
-
baseUrl = `https://github.com/${options.owner}/${options.repo}`;
|
|
88
|
+
group.refs.push(ref);
|
|
150
89
|
} else {
|
|
151
|
-
|
|
90
|
+
groups.push({ repo: null, refs: [ref] });
|
|
152
91
|
}
|
|
153
|
-
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
154
|
-
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
155
|
-
|
|
156
|
-
if (isWildcardPath(path)) {
|
|
157
|
-
const lastSlashIndex = normalizedPath.lastIndexOf('/');
|
|
158
|
-
if (lastSlashIndex > 0) {
|
|
159
|
-
const folderPath = normalizedPath.slice(0, lastSlashIndex);
|
|
160
|
-
return `${normalizedBaseUrl}/tree/${defaultBranch}${folderPath}`;
|
|
161
|
-
}
|
|
162
|
-
return `${normalizedBaseUrl}/tree/${defaultBranch}`;
|
|
163
|
-
}
|
|
164
|
-
return `${normalizedBaseUrl}/blob/${defaultBranch}${normalizedPath}`;
|
|
165
92
|
}
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
93
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
*/
|
|
172
|
-
function toggleGroup(repository) {
|
|
173
|
-
if (expandedGroups.has(repository)) {
|
|
174
|
-
expandedGroups.delete(repository);
|
|
175
|
-
} else {
|
|
176
|
-
expandedGroups.add(repository);
|
|
177
|
-
}
|
|
178
|
-
expandedGroups = expandedGroups; // Trigger reactivity
|
|
179
|
-
}
|
|
94
|
+
return groups;
|
|
95
|
+
})();
|
|
180
96
|
|
|
181
|
-
const DEFAULT_VISIBLE_PATHS = 3;
|
|
182
97
|
|
|
183
98
|
/**
|
|
184
99
|
* Get the anthology node for a series, if it belongs to one
|
|
@@ -270,100 +185,68 @@
|
|
|
270
185
|
</section>
|
|
271
186
|
{/if}
|
|
272
187
|
|
|
273
|
-
{#if
|
|
188
|
+
{#if referenceItems.length > 0}
|
|
274
189
|
<section class="references">
|
|
275
190
|
<h2 class="references-title">References</h2>
|
|
276
191
|
<p class="references-subtitle">Where this concept appears in code and docs.</p>
|
|
277
192
|
|
|
278
|
-
|
|
279
|
-
{
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
{#if url}
|
|
322
|
-
<a class="reference-path-link sprig-link sprig-link--quiet" href={url} target="_blank" rel="noopener noreferrer">
|
|
323
|
-
<span class="reference-path-text">{path}</span>
|
|
324
|
-
{#if isWildcard}
|
|
325
|
-
<span class="reference-path-wildcard">(pattern)</span>
|
|
326
|
-
{/if}
|
|
327
|
-
</a>
|
|
328
|
-
{:else}
|
|
329
|
-
<span class="reference-path-text">{path}</span>
|
|
330
|
-
{#if isWildcard}
|
|
331
|
-
<span class="reference-path-wildcard">(pattern)</span>
|
|
193
|
+
<ul class="reference-list">
|
|
194
|
+
{#each referenceGroups as group}
|
|
195
|
+
<li class="reference-item">
|
|
196
|
+
{#if group.repo}
|
|
197
|
+
<a
|
|
198
|
+
class="reference-repo-pill sprig-link"
|
|
199
|
+
href={group.repo.url}
|
|
200
|
+
target="_blank"
|
|
201
|
+
rel="noopener noreferrer"
|
|
202
|
+
>
|
|
203
|
+
{group.repo.title || group.repo.name}
|
|
204
|
+
</a>
|
|
205
|
+
{/if}
|
|
206
|
+
<ul class="reference-group-list">
|
|
207
|
+
{#each group.refs as ref}
|
|
208
|
+
<li class="reference-group-item">
|
|
209
|
+
{#if ref.urls && ref.urls.length > 0}
|
|
210
|
+
{#if ref.urls.length === 1}
|
|
211
|
+
{@const label = makeLinkLabel(ref.urls[0], ref.paths?.[0])}
|
|
212
|
+
<div class="reference-row">
|
|
213
|
+
<a class="reference-path-link sprig-link" href={ref.urls[0]} target="_blank" rel="noopener noreferrer">
|
|
214
|
+
{label}
|
|
215
|
+
</a>
|
|
216
|
+
{#if ref.kind}
|
|
217
|
+
<span class="reference-kind">{ref.kind}</span>
|
|
218
|
+
{/if}
|
|
219
|
+
</div>
|
|
220
|
+
{:else}
|
|
221
|
+
<ul class="reference-links-list">
|
|
222
|
+
{#each ref.urls as url, index}
|
|
223
|
+
{@const label = makeLinkLabel(url, ref.paths?.[index])}
|
|
224
|
+
<li class="reference-links-item">
|
|
225
|
+
<div class="reference-row">
|
|
226
|
+
<a class="reference-path-link sprig-link" href={url} target="_blank" rel="noopener noreferrer">
|
|
227
|
+
{label}
|
|
228
|
+
</a>
|
|
229
|
+
{#if ref.kind && index === 0}
|
|
230
|
+
<span class="reference-kind">{ref.kind}</span>
|
|
231
|
+
{/if}
|
|
232
|
+
</div>
|
|
233
|
+
</li>
|
|
234
|
+
{/each}
|
|
235
|
+
</ul>
|
|
332
236
|
{/if}
|
|
333
237
|
{/if}
|
|
334
|
-
{#if
|
|
335
|
-
<p class="reference-description
|
|
238
|
+
{#if ref.describe?.normalized}
|
|
239
|
+
<p class="reference-description">{ref.describe.normalized}</p>
|
|
240
|
+
{/if}
|
|
241
|
+
{#if ref.note?.normalized}
|
|
242
|
+
<p class="reference-note">{ref.note.normalized}</p>
|
|
336
243
|
{/if}
|
|
337
244
|
</li>
|
|
338
245
|
{/each}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
{/if}
|
|
344
|
-
{/each}
|
|
345
|
-
</ul>
|
|
346
|
-
|
|
347
|
-
{#if hiddenCount > 0 && !isExpanded}
|
|
348
|
-
<button
|
|
349
|
-
class="reference-expand-button"
|
|
350
|
-
on:click={() => toggleGroup(group.repository)}
|
|
351
|
-
type="button"
|
|
352
|
-
>
|
|
353
|
-
+ {hiddenCount} more path{hiddenCount === 1 ? '' : 's'}
|
|
354
|
-
</button>
|
|
355
|
-
{/if}
|
|
356
|
-
{#if isExpanded && hiddenCount > 0}
|
|
357
|
-
<button
|
|
358
|
-
class="reference-expand-button"
|
|
359
|
-
on:click={() => toggleGroup(group.repository)}
|
|
360
|
-
type="button"
|
|
361
|
-
>
|
|
362
|
-
Show less
|
|
363
|
-
</button>
|
|
364
|
-
{/if}
|
|
365
|
-
</div>
|
|
366
|
-
{/each}
|
|
246
|
+
</ul>
|
|
247
|
+
</li>
|
|
248
|
+
{/each}
|
|
249
|
+
</ul>
|
|
367
250
|
</section>
|
|
368
251
|
{/if}
|
|
369
252
|
|
|
@@ -450,13 +333,13 @@
|
|
|
450
333
|
|
|
451
334
|
.relationship-label {
|
|
452
335
|
font-family: var(--font-ui);
|
|
453
|
-
font-size: var(--sp-font-
|
|
336
|
+
font-size: var(--sp-font-small);
|
|
454
337
|
color: var(--text-secondary);
|
|
455
338
|
}
|
|
456
339
|
|
|
457
340
|
.relationship-separator {
|
|
458
341
|
font-family: var(--font-ui);
|
|
459
|
-
font-size: var(--sp-font-
|
|
342
|
+
font-size: var(--sp-font-small);
|
|
460
343
|
color: var(--text-tertiary);
|
|
461
344
|
opacity: 0.6;
|
|
462
345
|
}
|
|
@@ -510,101 +393,59 @@
|
|
|
510
393
|
margin: 0 0 1.5rem 0;
|
|
511
394
|
}
|
|
512
395
|
|
|
513
|
-
.reference-
|
|
396
|
+
.reference-list {
|
|
397
|
+
list-style: none;
|
|
398
|
+
padding: 0;
|
|
399
|
+
margin: 0;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.reference-item {
|
|
514
403
|
margin-bottom: 2rem;
|
|
515
404
|
}
|
|
516
405
|
|
|
517
|
-
.reference-
|
|
406
|
+
.reference-item:last-child {
|
|
518
407
|
margin-bottom: 0;
|
|
519
408
|
}
|
|
520
409
|
|
|
410
|
+
|
|
521
411
|
.reference-repo-pill {
|
|
522
412
|
display: inline-flex;
|
|
523
413
|
align-items: center;
|
|
524
414
|
gap: 6px;
|
|
525
|
-
padding:
|
|
526
|
-
|
|
527
|
-
border-radius: 6px;
|
|
528
|
-
text-decoration: none;
|
|
529
|
-
color: inherit;
|
|
530
|
-
margin-bottom: 0.75rem;
|
|
531
|
-
transition: opacity 0.2s;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
.reference-repo-pill:hover {
|
|
535
|
-
opacity: 0.8;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
.reference-repo-pill--no-link {
|
|
539
|
-
cursor: default;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
.reference-repo-pill--no-link:hover {
|
|
543
|
-
opacity: 1;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
.reference-repo-label {
|
|
415
|
+
padding: 2px 10px;
|
|
416
|
+
border-radius: 999px;
|
|
547
417
|
font-family: var(--font-ui);
|
|
548
|
-
font-size: var(--sp-font-
|
|
549
|
-
letter-spacing: 0.05em;
|
|
418
|
+
font-size: var(--sp-font-small);
|
|
550
419
|
text-transform: lowercase;
|
|
551
420
|
color: var(--text-tertiary);
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
.reference-repo-name {
|
|
556
|
-
font-family: var(--font-ui);
|
|
557
|
-
font-size: var(--sp-font-tiny);
|
|
558
|
-
color: var(--text-secondary);
|
|
559
|
-
font-weight: 400;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
.reference-paths-list {
|
|
563
|
-
list-style: none;
|
|
564
|
-
padding: 0;
|
|
565
|
-
margin: 0;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
.reference-path-item {
|
|
569
|
-
margin-bottom: 0.75rem;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
.reference-path-item:last-child {
|
|
573
|
-
margin-bottom: 0;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
.reference-path-item--tight {
|
|
577
|
-
margin-bottom: 0.25rem;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
.reference-path-item--spaced {
|
|
581
|
-
margin-top: 0.75rem;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
.reference-path-item--describe {
|
|
585
|
-
margin-top: 0.5rem;
|
|
586
|
-
margin-bottom: 0.75rem;
|
|
421
|
+
border: 1px solid var(--hairline);
|
|
422
|
+
margin-bottom: 1rem;
|
|
423
|
+
text-decoration: none;
|
|
587
424
|
}
|
|
588
425
|
|
|
589
426
|
.reference-path-link {
|
|
590
427
|
display: block;
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
.reference-path-text {
|
|
428
|
+
margin-top: 2px;
|
|
595
429
|
font-family: ui-monospace, 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
596
430
|
font-size: var(--sp-font-body);
|
|
597
431
|
color: inherit;
|
|
598
|
-
|
|
599
|
-
|
|
432
|
+
text-decoration: underline;
|
|
433
|
+
text-decoration-thickness: 1px;
|
|
434
|
+
text-underline-offset: 0.1875rem;
|
|
435
|
+
text-decoration-color: var(--sprig-link-underline);
|
|
600
436
|
}
|
|
601
437
|
|
|
602
|
-
.reference-
|
|
438
|
+
.reference-row {
|
|
439
|
+
display: flex;
|
|
440
|
+
align-items: baseline;
|
|
441
|
+
gap: 8px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.reference-kind {
|
|
445
|
+
margin-left: auto;
|
|
603
446
|
font-family: var(--font-ui);
|
|
604
|
-
font-size: var(--sp-font-
|
|
447
|
+
font-size: var(--sp-font-small);
|
|
605
448
|
color: var(--text-tertiary);
|
|
606
|
-
margin-left: 4px;
|
|
607
|
-
font-weight: 400;
|
|
608
449
|
}
|
|
609
450
|
|
|
610
451
|
.reference-description {
|
|
@@ -616,29 +457,37 @@
|
|
|
616
457
|
max-width: 70ch;
|
|
617
458
|
}
|
|
618
459
|
|
|
619
|
-
.reference-
|
|
620
|
-
margin
|
|
460
|
+
.reference-note {
|
|
461
|
+
margin: 6px 0 0 0;
|
|
462
|
+
font-family: var(--font-prose);
|
|
463
|
+
font-size: var(--sp-font-body);
|
|
464
|
+
color: var(--text-secondary);
|
|
465
|
+
line-height: 1.5;
|
|
466
|
+
max-width: 70ch;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.reference-links-list {
|
|
470
|
+
list-style: none;
|
|
471
|
+
padding: 0;
|
|
472
|
+
margin: 8px 0 0 0;
|
|
621
473
|
}
|
|
622
474
|
|
|
623
|
-
.reference-
|
|
475
|
+
.reference-group-list {
|
|
476
|
+
list-style: none;
|
|
477
|
+
padding: 0;
|
|
624
478
|
margin: 0;
|
|
625
479
|
}
|
|
626
480
|
|
|
627
|
-
.reference-
|
|
628
|
-
|
|
629
|
-
border: none;
|
|
630
|
-
color: var(--text-tertiary);
|
|
631
|
-
font-family: var(--font-ui);
|
|
632
|
-
font-size: var(--sp-font-tiny);
|
|
633
|
-
padding: 4px 0;
|
|
634
|
-
margin-top: 0.5rem;
|
|
635
|
-
cursor: pointer;
|
|
636
|
-
text-decoration: underline;
|
|
637
|
-
text-underline-offset: 2px;
|
|
481
|
+
.reference-group-item + .reference-group-item {
|
|
482
|
+
margin-top: 2rem;
|
|
638
483
|
}
|
|
639
484
|
|
|
640
|
-
.reference-
|
|
641
|
-
|
|
485
|
+
.reference-links-item {
|
|
486
|
+
margin-bottom: 6px;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.reference-links-item:last-child {
|
|
490
|
+
margin-bottom: 0;
|
|
642
491
|
}
|
|
643
492
|
|
|
644
493
|
@media (max-width: 768px) {
|
|
@@ -655,3 +504,4 @@
|
|
|
655
504
|
}
|
|
656
505
|
</style>
|
|
657
506
|
|
|
507
|
+
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { get } from 'svelte/store';
|
|
2
|
-
import { universeGraph } from '../data/universeStore.js';
|
|
3
|
-
import { isWildcardPath } from './isWildcardPath.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Builds a URL for a file path in a repository
|
|
7
|
-
* @param {string} repository - Repository key (e.g., "amaranthine")
|
|
8
|
-
* @param {string} path - File path (e.g., "/backends/api/src/routers/players.js" or "/data/actions/*.yaml")
|
|
9
|
-
* @returns {string | null} - Full URL to the file/folder, or null if repository is unconfigured
|
|
10
|
-
*/
|
|
11
|
-
export function linkForPath(repository, path) {
|
|
12
|
-
const graph = get(universeGraph);
|
|
13
|
-
if (!graph?.repositories) {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const repoConfig = graph.repositories[repository];
|
|
18
|
-
if (!repoConfig) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const { kind, options } = repoConfig;
|
|
23
|
-
const defaultBranch = options?.defaultBranch || 'main';
|
|
24
|
-
|
|
25
|
-
// Handle GitHub repositories
|
|
26
|
-
if (kind === 'sprig-repository-github') {
|
|
27
|
-
// Prefer url if available, otherwise build from owner/repo
|
|
28
|
-
let baseUrl;
|
|
29
|
-
if (options?.url) {
|
|
30
|
-
baseUrl = options.url;
|
|
31
|
-
} else {
|
|
32
|
-
const owner = options?.owner;
|
|
33
|
-
const repo = options?.repo;
|
|
34
|
-
if (!owner || !repo) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
baseUrl = `https://github.com/${owner}/${repo}`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
41
|
-
|
|
42
|
-
// Ensure path begins with /
|
|
43
|
-
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
44
|
-
|
|
45
|
-
// Handle wildcard paths - link to folder view
|
|
46
|
-
if (isWildcardPath(path)) {
|
|
47
|
-
// Derive folder by taking everything up to the last '/'
|
|
48
|
-
// e.g., "/data/actions/*.yaml" -> "/data/actions"
|
|
49
|
-
const lastSlashIndex = normalizedPath.lastIndexOf('/');
|
|
50
|
-
if (lastSlashIndex > 0) {
|
|
51
|
-
const folderPath = normalizedPath.slice(0, lastSlashIndex);
|
|
52
|
-
// Build GitHub tree URL: baseUrl/tree/branch/folder
|
|
53
|
-
return `${normalizedBaseUrl}/tree/${defaultBranch}${folderPath}`;
|
|
54
|
-
}
|
|
55
|
-
// Fallback: if no slash, link to repo root
|
|
56
|
-
return `${normalizedBaseUrl}/tree/${defaultBranch}`;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Build GitHub blob URL: baseUrl/blob/branch/path
|
|
60
|
-
return `${normalizedBaseUrl}/blob/${defaultBranch}${normalizedPath}`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { get } from 'svelte/store';
|
|
2
|
-
import { universeGraph } from '../data/universeStore.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Builds a URL for a repository root
|
|
6
|
-
* @param {string} repository - Repository key (e.g., "amaranthine")
|
|
7
|
-
* @returns {string | null} - Full URL to the repository root, or null if repository is unconfigured
|
|
8
|
-
*/
|
|
9
|
-
export function linkForRepository(repository) {
|
|
10
|
-
const graph = get(universeGraph);
|
|
11
|
-
if (!graph?.repositories) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const repoConfig = graph.repositories[repository];
|
|
16
|
-
if (!repoConfig) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { kind, options } = repoConfig;
|
|
21
|
-
|
|
22
|
-
// Handle GitHub repositories
|
|
23
|
-
if (kind === 'sprig-repository-github') {
|
|
24
|
-
// Prefer url if available, otherwise build from owner/repo
|
|
25
|
-
if (options?.url) {
|
|
26
|
-
const baseUrl = options.url;
|
|
27
|
-
// Remove trailing slash if present
|
|
28
|
-
return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const owner = options?.owner;
|
|
32
|
-
const repo = options?.repo;
|
|
33
|
-
if (owner && repo) {
|
|
34
|
-
const baseUrl = `https://github.com/${owner}/${repo}`;
|
|
35
|
-
// Remove trailing slash if present
|
|
36
|
-
return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|