@gsa-tts/graymatter-ui 0.3.17 → 0.3.19
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/README.md +11 -13
- package/dist/graymatter-ui.js +1565 -1599
- package/dist/graymatter-ui.umd.cjs +6 -6
- package/package.json +1 -1
- package/src/components/ApiDocSubNavMenu.svelte +78 -68
- package/src/components/DesktopSideNav.svelte +4 -11
- package/src/components/MobileBottomNav.svelte +4 -11
- package/src/layouts/GlobalAppLayout.astro +19 -1
- package/src/utils/getAppUrls.ts +84 -0
package/package.json
CHANGED
|
@@ -148,9 +148,9 @@
|
|
|
148
148
|
parent: SubNavItem | null
|
|
149
149
|
): SubNavItem | null {
|
|
150
150
|
for (const it of items) {
|
|
151
|
-
if (it.id === item.id) return parent
|
|
152
|
-
if (it.children) {
|
|
153
|
-
const found = search(it.children,
|
|
151
|
+
if (it.id === item.id) return parent || it;
|
|
152
|
+
if (it.children && it.children.length > 0) {
|
|
153
|
+
const found = search(it.children, it);
|
|
154
154
|
if (found) return found;
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -179,6 +179,7 @@
|
|
|
179
179
|
|
|
180
180
|
let currentSelectedId = $state<string | null>(null);
|
|
181
181
|
let hydrated = $state(false);
|
|
182
|
+
let pageLoadComplete = $state(false);
|
|
182
183
|
|
|
183
184
|
const effectiveSelectedId = $derived(() => {
|
|
184
185
|
if (!hydrated || !data) return null;
|
|
@@ -195,28 +196,28 @@
|
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
// Second priority: sessionStorage
|
|
199
|
+
// Second priority: Check if we have a persisted selectedId from sessionStorage
|
|
199
200
|
if (typeof window !== 'undefined') {
|
|
200
201
|
const storedId = sessionStorage.getItem('subNav-selectedId');
|
|
201
202
|
if (storedId) {
|
|
202
203
|
try {
|
|
203
204
|
const parsedId = JSON.parse(storedId);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return targetItem.id;
|
|
208
|
-
}
|
|
205
|
+
const targetItem = findItemById(data, parsedId);
|
|
206
|
+
if (targetItem) {
|
|
207
|
+
return parsedId;
|
|
209
208
|
}
|
|
210
|
-
} catch
|
|
211
|
-
|
|
209
|
+
} catch {
|
|
210
|
+
// Invalid JSON, ignore
|
|
212
211
|
}
|
|
213
212
|
}
|
|
214
213
|
}
|
|
215
214
|
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
// Fallback: use first item when no other selection method works
|
|
216
|
+
if (pageLoadComplete) {
|
|
217
|
+
const firstItem = findFirstItem(data);
|
|
218
|
+
if (firstItem && firstItem.title) {
|
|
219
|
+
return firstItem.id;
|
|
220
|
+
}
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
return null;
|
|
@@ -226,6 +227,27 @@
|
|
|
226
227
|
$effect(() => {
|
|
227
228
|
if (effectiveSelectedId() && effectiveSelectedId() !== currentSelectedId) {
|
|
228
229
|
currentSelectedId = effectiveSelectedId();
|
|
230
|
+
|
|
231
|
+
// When restoring from sessionStorage, also restore the correct header
|
|
232
|
+
// Only do this if we're on the API documentation page (where this component exists)
|
|
233
|
+
if (data && effectiveSelectedId() && typeof window !== 'undefined') {
|
|
234
|
+
const currentPath = window.location.pathname;
|
|
235
|
+
const isApiDocumentationPage =
|
|
236
|
+
currentPath.includes('/api/documentation');
|
|
237
|
+
|
|
238
|
+
if (isApiDocumentationPage) {
|
|
239
|
+
const restoredItem = findItemById(data, effectiveSelectedId()!);
|
|
240
|
+
if (restoredItem) {
|
|
241
|
+
const topLevel =
|
|
242
|
+
findTopLevelParent(restoredItem, data) || restoredItem;
|
|
243
|
+
import('@gsa-tts/graymatter-ui/stores/navigationStore')
|
|
244
|
+
.then(({ selectedSubNavItemTitle }) => {
|
|
245
|
+
selectedSubNavItemTitle.set(topLevel.title || '');
|
|
246
|
+
})
|
|
247
|
+
.catch();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
229
251
|
}
|
|
230
252
|
});
|
|
231
253
|
|
|
@@ -234,53 +256,46 @@
|
|
|
234
256
|
|
|
235
257
|
if (!data) return;
|
|
236
258
|
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
JSON.stringify(topLevel.id)
|
|
255
|
-
);
|
|
256
|
-
sessionStorage.setItem(
|
|
257
|
-
'subNav-topLevelTitle',
|
|
258
|
-
JSON.stringify(topLevel.title || '')
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
})
|
|
262
|
-
.catch();
|
|
263
|
-
|
|
264
|
-
// Handle scrolling if we have a URL hash
|
|
265
|
-
if (targetItem.href && targetItem.href.includes('#')) {
|
|
266
|
-
const hash = targetItem.href.split('#')[1];
|
|
267
|
-
if (hash) {
|
|
268
|
-
setTimeout(() => scrollToAnchorWithOffset(hash), 100);
|
|
259
|
+
// Check if we're navigating TO the documentation page (vs refreshing while on it)
|
|
260
|
+
if (typeof window !== 'undefined') {
|
|
261
|
+
const currentPath = window.location.pathname;
|
|
262
|
+
const isDocumentationPage = currentPath.includes('/api/documentation');
|
|
263
|
+
|
|
264
|
+
// If we're on the documentation page, check if we came from a different page
|
|
265
|
+
if (isDocumentationPage) {
|
|
266
|
+
const referrer = document.referrer;
|
|
267
|
+
const isFromDifferentPage =
|
|
268
|
+
referrer && !referrer.includes('/api/documentation');
|
|
269
|
+
|
|
270
|
+
// If we came from a different page, clear sessionStorage to reset to initial values
|
|
271
|
+
if (isFromDifferentPage) {
|
|
272
|
+
sessionStorage.removeItem('subNav-selectedId');
|
|
273
|
+
sessionStorage.removeItem('subNav-selectedTitle');
|
|
274
|
+
sessionStorage.removeItem('subNav-topLevelId');
|
|
275
|
+
sessionStorage.removeItem('subNav-topLevelTitle');
|
|
269
276
|
}
|
|
270
277
|
}
|
|
271
278
|
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
// Clean up any leftover sessionStorage items
|
|
280
|
+
sessionStorage.removeItem('__subNavPageLoaded');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Reset currentSelectedId to ensure clean state
|
|
284
|
+
currentSelectedId = null;
|
|
285
|
+
|
|
286
|
+
// Handle scrolling if we have a URL hash
|
|
287
|
+
const targetItem = currentSelectedId
|
|
288
|
+
? findItemById(data, currentSelectedId)
|
|
289
|
+
: null;
|
|
290
|
+
if (targetItem && targetItem.href && targetItem.href.includes('#')) {
|
|
291
|
+
const hash = targetItem.href.split('#')[1];
|
|
292
|
+
if (hash) {
|
|
293
|
+
setTimeout(() => scrollToAnchorWithOffset(hash), 100);
|
|
282
294
|
}
|
|
283
295
|
}
|
|
296
|
+
|
|
297
|
+
// Mark page load as complete
|
|
298
|
+
pageLoadComplete = true;
|
|
284
299
|
subNavReady.set(true);
|
|
285
300
|
});
|
|
286
301
|
|
|
@@ -293,22 +308,17 @@
|
|
|
293
308
|
}
|
|
294
309
|
currentSelectedId = item.id;
|
|
295
310
|
const topLevel = findTopLevelParent(item, data) || item;
|
|
311
|
+
|
|
296
312
|
import('@gsa-tts/graymatter-ui/stores/navigationStore')
|
|
297
313
|
.then(({ selectedSubNavItemTitle }) => {
|
|
298
314
|
selectedSubNavItemTitle.set(topLevel.title || '');
|
|
299
|
-
if (typeof window !== 'undefined') {
|
|
300
|
-
sessionStorage.setItem('subNav-selectedId', JSON.stringify(item.id));
|
|
301
|
-
sessionStorage.setItem(
|
|
302
|
-
'subNav-topLevelId',
|
|
303
|
-
JSON.stringify(topLevel.id)
|
|
304
|
-
);
|
|
305
|
-
sessionStorage.setItem(
|
|
306
|
-
'subNav-topLevelTitle',
|
|
307
|
-
JSON.stringify(topLevel.title || '')
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
315
|
})
|
|
311
316
|
.catch();
|
|
317
|
+
|
|
318
|
+
// Save selectedId to sessionStorage for persistence
|
|
319
|
+
if (typeof window !== 'undefined') {
|
|
320
|
+
sessionStorage.setItem('subNav-selectedId', JSON.stringify(item.id));
|
|
321
|
+
}
|
|
312
322
|
if (typeof window !== 'undefined') {
|
|
313
323
|
if (item.href && item.href.includes('#')) {
|
|
314
324
|
const hash = item.href.split('#')[1];
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
import ApiIcon from './icons/ApiIcon.svelte';
|
|
20
20
|
import MenuIcon from './icons/MenuIcon.svelte';
|
|
21
21
|
import ProfileMenu from './ProfileMenu.svelte';
|
|
22
|
-
import { getAppUrls } from '../utils/getAppUrls';
|
|
23
22
|
// import HelpIcon from './icons/HelpIcon.svelte';
|
|
24
23
|
|
|
25
24
|
const {
|
|
@@ -35,12 +34,6 @@
|
|
|
35
34
|
discoverUrl,
|
|
36
35
|
} = $props();
|
|
37
36
|
|
|
38
|
-
// Only call getAppUrls if not all URLs are provided via props
|
|
39
|
-
const urls =
|
|
40
|
-
apiUrl && chatUrl && consoleUrl && discoverUrl
|
|
41
|
-
? { api: '#', chat: '#', console: '#', discover: '#' }
|
|
42
|
-
: getAppUrls();
|
|
43
|
-
|
|
44
37
|
let hydrated = false;
|
|
45
38
|
let navContainer: HTMLDivElement;
|
|
46
39
|
let hoveredItem = $state<string | null>(null);
|
|
@@ -332,7 +325,7 @@
|
|
|
332
325
|
<div class="nav-items" part="nav-items">
|
|
333
326
|
{#if !hideDiscover}
|
|
334
327
|
<a
|
|
335
|
-
href={discoverUrl ||
|
|
328
|
+
href={discoverUrl || '#'}
|
|
336
329
|
class="nav-item discover-item"
|
|
337
330
|
class:selected={isNavSelected('discover')}
|
|
338
331
|
class:hovered={isNavHovered('discover')}
|
|
@@ -356,7 +349,7 @@
|
|
|
356
349
|
</a>
|
|
357
350
|
{/if}
|
|
358
351
|
<a
|
|
359
|
-
href={chatUrl ||
|
|
352
|
+
href={chatUrl || '#'}
|
|
360
353
|
class="nav-item chat-item"
|
|
361
354
|
class:selected={isNavSelected('chat')}
|
|
362
355
|
class:hovered={isNavHovered('chat')}
|
|
@@ -380,7 +373,7 @@
|
|
|
380
373
|
</a>
|
|
381
374
|
|
|
382
375
|
<a
|
|
383
|
-
href={consoleUrl ||
|
|
376
|
+
href={consoleUrl || '#'}
|
|
384
377
|
class="nav-item console-item"
|
|
385
378
|
class:selected={isNavSelected('console')}
|
|
386
379
|
class:hovered={isNavHovered('console')}
|
|
@@ -404,7 +397,7 @@
|
|
|
404
397
|
</a>
|
|
405
398
|
|
|
406
399
|
<a
|
|
407
|
-
href={apiUrl ||
|
|
400
|
+
href={apiUrl || '#'}
|
|
408
401
|
class="nav-item api-item"
|
|
409
402
|
class:selected={isNavSelected('api')}
|
|
410
403
|
class:hovered={isNavHovered('api')}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import ChatIcon from './icons/ChatIcon.svelte';
|
|
6
6
|
import ConsoleIcon from './icons/ConsoleIcon.svelte';
|
|
7
7
|
import ApiIcon from './icons/ApiIcon.svelte';
|
|
8
|
-
import { getAppUrls } from '../utils/getAppUrls';
|
|
9
8
|
import slugify from 'slugify';
|
|
10
9
|
|
|
11
10
|
let {
|
|
@@ -29,27 +28,21 @@
|
|
|
29
28
|
discoverUrl?: string;
|
|
30
29
|
} = $props();
|
|
31
30
|
|
|
32
|
-
// Only call getAppUrls if not all URLs are provided via props
|
|
33
|
-
const urls =
|
|
34
|
-
apiUrl && chatUrl && consoleUrl && discoverUrl
|
|
35
|
-
? { api: '#', chat: '#', console: '#', discover: '#' }
|
|
36
|
-
: getAppUrls();
|
|
37
|
-
|
|
38
31
|
const allNavItems = [
|
|
39
32
|
{
|
|
40
33
|
id: 'discover',
|
|
41
34
|
label: 'Discover',
|
|
42
35
|
icon: DiscoverIcon,
|
|
43
|
-
href: discoverUrl ||
|
|
36
|
+
href: discoverUrl || '#',
|
|
44
37
|
},
|
|
45
|
-
{ id: 'chat', label: 'Chat', icon: ChatIcon, href: chatUrl ||
|
|
38
|
+
{ id: 'chat', label: 'Chat', icon: ChatIcon, href: chatUrl || '#' },
|
|
46
39
|
{
|
|
47
40
|
id: 'console',
|
|
48
41
|
label: 'Console',
|
|
49
42
|
icon: ConsoleIcon,
|
|
50
|
-
href: consoleUrl ||
|
|
43
|
+
href: consoleUrl || '#',
|
|
51
44
|
},
|
|
52
|
-
{ id: 'api', label: 'API', icon: ApiIcon, href: apiUrl ||
|
|
45
|
+
{ id: 'api', label: 'API', icon: ApiIcon, href: apiUrl || '#' },
|
|
53
46
|
] as const;
|
|
54
47
|
|
|
55
48
|
// Filter out discover item if hideDiscover is true
|
|
@@ -8,6 +8,7 @@ import MobileBottomNav from '../components/MobileBottomNav.svelte';
|
|
|
8
8
|
import MobileSideMenu from '../components/MobileSideMenu.svelte';
|
|
9
9
|
import Logo from '../components/Logo.svelte';
|
|
10
10
|
import { siteName } from '../constants';
|
|
11
|
+
import { getAppUrls } from '../utils/getAppUrls';
|
|
11
12
|
|
|
12
13
|
const {
|
|
13
14
|
componentOptions = {},
|
|
@@ -21,6 +22,8 @@ const {
|
|
|
21
22
|
description,
|
|
22
23
|
openGraphImage,
|
|
23
24
|
} = Astro.props;
|
|
25
|
+
|
|
26
|
+
const { api, chat, console, discover } = getAppUrls();
|
|
24
27
|
---
|
|
25
28
|
|
|
26
29
|
<BaseLayout title={pageTitle} {gtmID} {description} {openGraphImage}>
|
|
@@ -33,6 +36,10 @@ const {
|
|
|
33
36
|
ssrSelectedItem={ssrSelectedItem}
|
|
34
37
|
{showAppIcons}
|
|
35
38
|
{profileMenuData}
|
|
39
|
+
apiUrl={api}
|
|
40
|
+
chatUrl={chat}
|
|
41
|
+
consoleUrl={console}
|
|
42
|
+
discoverUrl={discover}
|
|
36
43
|
>
|
|
37
44
|
<slot name="sub-nav" />
|
|
38
45
|
</DesktopSideNav>
|
|
@@ -54,11 +61,22 @@ const {
|
|
|
54
61
|
{profileMenuData}
|
|
55
62
|
{logoLinkUrl}
|
|
56
63
|
{logoLinkLabel}
|
|
64
|
+
apiUrl={api}
|
|
65
|
+
chatUrl={chat}
|
|
66
|
+
consoleUrl={console}
|
|
67
|
+
discoverUrl={discover}
|
|
57
68
|
/>
|
|
58
69
|
<MobileSideMenu client:load {logoLinkUrl} {logoLinkLabel}>
|
|
59
70
|
<slot name="sub-nav" />
|
|
60
71
|
</MobileSideMenu>
|
|
61
|
-
<MobileBottomNav
|
|
72
|
+
<MobileBottomNav
|
|
73
|
+
client:load
|
|
74
|
+
{showAppIcons}
|
|
75
|
+
apiUrl={api}
|
|
76
|
+
chatUrl={chat}
|
|
77
|
+
consoleUrl={console}
|
|
78
|
+
discoverUrl={discover}
|
|
79
|
+
/>
|
|
62
80
|
</nav>
|
|
63
81
|
|
|
64
82
|
<!-- Fixed Logo (Desktop only) -->
|
package/src/utils/getAppUrls.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { getUrlFromBase } from '@gsa-tts/graymatter-ui/helpers/url.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Logs a warning message when an environment variable is missing.
|
|
5
|
+
*
|
|
6
|
+
* @param name - The name of the missing environment variable
|
|
7
|
+
* @param value - The value of the environment variable (should be undefined)
|
|
8
|
+
*/
|
|
3
9
|
function warnIfMissing(name: string, value: string | undefined) {
|
|
4
10
|
if (!value) {
|
|
5
11
|
console.warn(`[getAppUrls] Environment variable for ${name} is missing!`);
|
|
6
12
|
}
|
|
7
13
|
}
|
|
8
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Checks if a URL is absolute (starts with protocol like http:// or https://).
|
|
17
|
+
*
|
|
18
|
+
* @param url - The URL string to check
|
|
19
|
+
* @returns True if the URL is absolute, false otherwise
|
|
20
|
+
*/
|
|
9
21
|
function isAbsoluteUrl(url: string): boolean {
|
|
10
22
|
try {
|
|
11
23
|
new URL(url);
|
|
@@ -15,6 +27,24 @@ function isAbsoluteUrl(url: string): boolean {
|
|
|
15
27
|
}
|
|
16
28
|
}
|
|
17
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Processes a URL string, handling both absolute and relative URLs.
|
|
32
|
+
*
|
|
33
|
+
* If the URL is undefined, returns the fallback. If it's absolute (starts with protocol),
|
|
34
|
+
* returns it as-is. If it's relative, processes it through `getUrlFromBase()` to create
|
|
35
|
+
* a full URL.
|
|
36
|
+
*
|
|
37
|
+
* @param url - The URL string to process (can be undefined)
|
|
38
|
+
* @param fallback - The fallback URL to use if the input URL is undefined
|
|
39
|
+
* @returns The processed URL string (can be absolute URL or relative path)
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* processUrl('https://example.com', '/fallback'); // "https://example.com"
|
|
44
|
+
* processUrl('/api/docs', '/fallback'); // "/api/docs" (processed by getUrlFromBase)
|
|
45
|
+
* processUrl(undefined, '/fallback'); // "/fallback"
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
18
48
|
function processUrl(url: string | undefined, fallback: string): string {
|
|
19
49
|
if (!url) {
|
|
20
50
|
return fallback;
|
|
@@ -27,6 +57,60 @@ function processUrl(url: string | undefined, fallback: string): string {
|
|
|
27
57
|
return getUrlFromBase(url);
|
|
28
58
|
}
|
|
29
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Retrieves application URLs/paths from environment variables with fallback support.
|
|
62
|
+
*
|
|
63
|
+
* This utility function processes environment variables to provide URLs or paths for different
|
|
64
|
+
* application sections (chat, console, API, discover). It supports both absolute URLs
|
|
65
|
+
* and relative paths, with intelligent fallback handling.
|
|
66
|
+
*
|
|
67
|
+
* @param envArg - Optional environment object to use instead of `import.meta.env`.
|
|
68
|
+
* Useful for testing or when you want to override environment variables.
|
|
69
|
+
* If not provided, uses Vite's `import.meta.env`.
|
|
70
|
+
*
|
|
71
|
+
* @returns An object containing processed URLs/paths for each application section:
|
|
72
|
+
* - `chat`: Chat application URL or path
|
|
73
|
+
* - `console`: Console application URL or path
|
|
74
|
+
* - `api`: API documentation URL or path
|
|
75
|
+
* - `discover`: Discover page URL or path
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* // Using default environment variables
|
|
80
|
+
* const urls = getAppUrls();
|
|
81
|
+
* console.log(urls.chat); // "#" (fallback) or "https://chat.example.com" (URL)
|
|
82
|
+
*
|
|
83
|
+
* // Using custom environment object (useful for testing)
|
|
84
|
+
* const urls = getAppUrls({
|
|
85
|
+
* PUBLIC_CHAT_URL: 'https://chat.dev/',
|
|
86
|
+
* PUBLIC_CONSOLE_URL: 'https://console.dev/',
|
|
87
|
+
* PUBLIC_API_URL: 'https://api.dev/docs',
|
|
88
|
+
* PUBLIC_DISCOVER_URL: 'https://discover.dev/'
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* // Environment variables (.env file)
|
|
95
|
+
* PUBLIC_CHAT_URL=https://chat.example.com
|
|
96
|
+
* PUBLIC_CONSOLE_URL=https://console.example.com
|
|
97
|
+
* PUBLIC_API_URL=/api/documentation
|
|
98
|
+
* PUBLIC_DISCOVER_URL=https://discover.example.com
|
|
99
|
+
*
|
|
100
|
+
* // Usage in code
|
|
101
|
+
* const { chat, console, api, discover } = getAppUrls();
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @remarks
|
|
105
|
+
* - URLs are processed through `processUrl()` which handles both absolute and relative URLs
|
|
106
|
+
* - Relative URLs are processed through `getUrlFromBase()` to create full URLs
|
|
107
|
+
* - Missing environment variables result in fallback values (`#` for most, `/api/documentation` for API)
|
|
108
|
+
* - Warnings are logged in development when environment variables are missing
|
|
109
|
+
* - The function is designed to work with Vite's `import.meta.env` system
|
|
110
|
+
*
|
|
111
|
+
* @see {@link processUrl} - URL processing logic
|
|
112
|
+
* @see {@link getUrlFromBase} - Base URL resolution
|
|
113
|
+
*/
|
|
30
114
|
export function getAppUrls(envArg?: Record<string, string | undefined>) {
|
|
31
115
|
const env = envArg || import.meta.env;
|
|
32
116
|
const fallback = '#';
|