@vertesia/ui 1.5.0-dev.20260714.072725Z → 1.5.0-dev.20260717.131047Z

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 (135) hide show
  1. package/lib/env/index.d.ts +19 -0
  2. package/lib/env/index.d.ts.map +1 -1
  3. package/lib/env/index.js +6 -0
  4. package/lib/env/index.js.map +1 -1
  5. package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
  6. package/lib/features/agent/chat/ArtifactsTab.js +65 -11
  7. package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
  8. package/lib/features/agent/chat/ModernAgentConversation.js +51 -48
  9. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  10. package/lib/features/agent/chat/hooks/useArtifacts.d.ts +8 -1
  11. package/lib/features/agent/chat/hooks/useArtifacts.d.ts.map +1 -1
  12. package/lib/features/agent/chat/hooks/useArtifacts.js +28 -10
  13. package/lib/features/agent/chat/hooks/useArtifacts.js.map +1 -1
  14. package/lib/features/document-viewer/UniversalDocumentViewer.d.ts +73 -0
  15. package/lib/features/document-viewer/UniversalDocumentViewer.d.ts.map +1 -0
  16. package/lib/features/document-viewer/UniversalDocumentViewer.js +415 -0
  17. package/lib/features/document-viewer/UniversalDocumentViewer.js.map +1 -0
  18. package/lib/features/document-viewer/index.d.ts +2 -0
  19. package/lib/features/document-viewer/index.d.ts.map +1 -0
  20. package/lib/features/document-viewer/index.js +2 -0
  21. package/lib/features/document-viewer/index.js.map +1 -0
  22. package/lib/features/index.d.ts +1 -0
  23. package/lib/features/index.d.ts.map +1 -1
  24. package/lib/features/index.js +1 -0
  25. package/lib/features/index.js.map +1 -1
  26. package/lib/features/permissions/UserPermissionsProvider.d.ts +2 -3
  27. package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
  28. package/lib/features/permissions/UserPermissionsProvider.js +31 -38
  29. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  30. package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  31. package/lib/features/store/objects/components/ContentOverview.js +73 -13
  32. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  33. package/lib/i18n/locales/ar.json +2 -0
  34. package/lib/i18n/locales/de.json +2 -0
  35. package/lib/i18n/locales/en.json +2 -0
  36. package/lib/i18n/locales/es.json +2 -0
  37. package/lib/i18n/locales/fr.json +2 -0
  38. package/lib/i18n/locales/it.json +2 -0
  39. package/lib/i18n/locales/ja.json +2 -0
  40. package/lib/i18n/locales/ko.json +2 -0
  41. package/lib/i18n/locales/pt.json +2 -0
  42. package/lib/i18n/locales/ru.json +2 -0
  43. package/lib/i18n/locales/tr.json +2 -0
  44. package/lib/i18n/locales/zh-TW.json +2 -0
  45. package/lib/i18n/locales/zh.json +2 -0
  46. package/lib/router/HistoryNavigator.d.ts.map +1 -1
  47. package/lib/router/HistoryNavigator.js +4 -1
  48. package/lib/router/HistoryNavigator.js.map +1 -1
  49. package/lib/router/Nav.d.ts.map +1 -1
  50. package/lib/router/Nav.js +7 -1
  51. package/lib/router/Nav.js.map +1 -1
  52. package/lib/router/Router.d.ts.map +1 -1
  53. package/lib/router/Router.js +19 -4
  54. package/lib/router/Router.js.map +1 -1
  55. package/lib/router/path.d.ts +15 -0
  56. package/lib/router/path.d.ts.map +1 -1
  57. package/lib/router/path.js +43 -0
  58. package/lib/router/path.js.map +1 -1
  59. package/lib/session/UserSession.d.ts +6 -0
  60. package/lib/session/UserSession.d.ts.map +1 -1
  61. package/lib/session/UserSession.js +31 -9
  62. package/lib/session/UserSession.js.map +1 -1
  63. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  64. package/lib/session/UserSessionProvider.js +111 -49
  65. package/lib/session/UserSessionProvider.js.map +1 -1
  66. package/lib/session/auth/composable.d.ts.map +1 -1
  67. package/lib/session/auth/composable.js +52 -3
  68. package/lib/session/auth/composable.js.map +1 -1
  69. package/lib/session/auth/domainRouting.d.ts +25 -0
  70. package/lib/session/auth/domainRouting.d.ts.map +1 -1
  71. package/lib/session/auth/domainRouting.js +36 -0
  72. package/lib/session/auth/domainRouting.js.map +1 -1
  73. package/lib/shell/login/SignInRestrictedEnvStep.d.ts +1 -1
  74. package/lib/shell/login/SignInRestrictedEnvStep.js +1 -1
  75. package/lib/shell/login/SigninScreen.js +9 -1
  76. package/lib/shell/login/SigninScreen.js.map +1 -1
  77. package/lib/shell/login/productionUrl.d.ts +0 -14
  78. package/lib/shell/login/productionUrl.d.ts.map +1 -1
  79. package/lib/shell/login/productionUrl.js +14 -5
  80. package/lib/shell/login/productionUrl.js.map +1 -1
  81. package/lib/vertesia-ui-core.js +3 -3
  82. package/lib/vertesia-ui-core.js.map +1 -1
  83. package/lib/vertesia-ui-env.js +1 -1
  84. package/lib/vertesia-ui-env.js.map +1 -1
  85. package/lib/vertesia-ui-features.js +28 -28
  86. package/lib/vertesia-ui-features.js.map +1 -1
  87. package/lib/vertesia-ui-i18n.js +13 -13
  88. package/lib/vertesia-ui-router.js +1 -1
  89. package/lib/vertesia-ui-router.js.map +1 -1
  90. package/lib/vertesia-ui-session.js +1 -1
  91. package/lib/vertesia-ui-session.js.map +1 -1
  92. package/lib/vertesia-ui-shell.js +1 -1
  93. package/lib/vertesia-ui-shell.js.map +1 -1
  94. package/lib/vertesia-ui-widgets.js +2 -2
  95. package/lib/vertesia-ui-widgets.js.map +1 -1
  96. package/llms.txt +70 -70
  97. package/package.json +8 -7
  98. package/src/env/index.ts +24 -0
  99. package/src/features/agent/chat/ArtifactsTab.tsx +155 -21
  100. package/src/features/agent/chat/ModernAgentConversation.tsx +53 -49
  101. package/src/features/agent/chat/hooks/useArtifacts.ts +44 -11
  102. package/src/features/document-viewer/UniversalDocumentViewer.test.tsx +123 -0
  103. package/src/features/document-viewer/UniversalDocumentViewer.tsx +746 -0
  104. package/src/features/document-viewer/index.ts +1 -0
  105. package/src/features/index.ts +1 -0
  106. package/src/features/permissions/UserPermissionsProvider.tsx +36 -39
  107. package/src/features/store/objects/components/ContentOverview.tsx +99 -26
  108. package/src/i18n/locales/ar.json +2 -0
  109. package/src/i18n/locales/de.json +2 -0
  110. package/src/i18n/locales/en.json +2 -0
  111. package/src/i18n/locales/es.json +2 -0
  112. package/src/i18n/locales/fr.json +2 -0
  113. package/src/i18n/locales/it.json +2 -0
  114. package/src/i18n/locales/ja.json +2 -0
  115. package/src/i18n/locales/ko.json +2 -0
  116. package/src/i18n/locales/pt.json +2 -0
  117. package/src/i18n/locales/ru.json +2 -0
  118. package/src/i18n/locales/tr.json +2 -0
  119. package/src/i18n/locales/zh-TW.json +2 -0
  120. package/src/i18n/locales/zh.json +2 -0
  121. package/src/router/HistoryNavigator.ts +4 -1
  122. package/src/router/Nav.tsx +7 -1
  123. package/src/router/Router.tsx +18 -4
  124. package/src/router/path.test.ts +87 -0
  125. package/src/router/path.ts +38 -0
  126. package/src/session/UserSession.ts +32 -9
  127. package/src/session/UserSessionProvider.tsx +138 -62
  128. package/src/session/auth/composable.test.ts +111 -0
  129. package/src/session/auth/composable.ts +58 -3
  130. package/src/session/auth/domainRouting.test.ts +28 -1
  131. package/src/session/auth/domainRouting.ts +38 -0
  132. package/src/shell/login/SignInRestrictedEnvStep.tsx +1 -1
  133. package/src/shell/login/SigninScreen.tsx +9 -1
  134. package/src/shell/login/productionUrl.test.ts +8 -5
  135. package/src/shell/login/productionUrl.ts +15 -5
@@ -0,0 +1 @@
1
+ export * from './UniversalDocumentViewer.js';
@@ -1,5 +1,6 @@
1
1
  export * from './activity-doc/index.js';
2
2
  export * from './agent';
3
+ export * from './document-viewer/index.js';
3
4
  export * from './errors/index.js';
4
5
  export * from './facets/index.js';
5
6
  export * from './layout/index.js';
@@ -1,7 +1,7 @@
1
- import { type Permission, PrincipalType, type SystemRoleDefinition } from '@vertesia/common';
1
+ import { type AuthTokenPayload, type Permission, PrincipalType, type SystemRoleDefinition } from '@vertesia/common';
2
2
  import { ErrorBox, errorMessage, useFetch } from '@vertesia/ui/core';
3
3
  import { useUITranslation } from '@vertesia/ui/i18n';
4
- import { type UserSession, useUserSession } from '@vertesia/ui/session';
4
+ import { useUserSession } from '@vertesia/ui/session';
5
5
  import { createContext, useContext, useMemo } from 'react';
6
6
  import { isAnyOf } from './helpers';
7
7
 
@@ -12,39 +12,18 @@ export class UserPermissions {
12
12
  roles: Set<string>; // all roles of the current user
13
13
  permissions: Set<string>; // all permissions of the current user
14
14
 
15
- constructor(session: UserSession, roles: ListRolesResponse) {
16
- if (!session.authToken) {
17
- throw new Error('No auth token found in user session');
18
- }
15
+ constructor(authToken: AuthTokenPayload, roles: ListRolesResponse = []) {
19
16
  this.system_roles = roles;
20
- const userRoles = new Set<string>(session.authToken.account_roles || []);
21
- if (session.authToken.project_roles) {
22
- for (const role of session.authToken.project_roles) {
23
- userRoles.add(role);
24
- }
25
- }
17
+ const roleNames = [...(authToken.account_roles || []), ...(authToken.project_roles || [])];
18
+ const userRoles = new Set<string>(roleNames);
26
19
  this.roles = userRoles;
27
- // build a temporary role to permissions map
28
- const map: Record<string, Permission[]> = {};
29
- for (const role of roles) {
30
- map[role.name] = role.permissions;
31
- }
20
+ const rolePermissions = authToken.permissions ?? getPermissionsForRolesFromMappings(roleNames, roles);
21
+ // OAuth access tokens are capped to the permissions granted to the token (its scopes).
32
22
  const permissionCap =
33
- session.authToken.type === PrincipalType.OAuthAccess
34
- ? new Set<string>(session.authToken.permissions ?? [])
35
- : undefined;
36
- const permissions = new Set<string>();
37
- for (const role of userRoles) {
38
- const rolePermissions = map[role];
39
- if (rolePermissions) {
40
- for (const permission of rolePermissions) {
41
- if (!permissionCap || permissionCap.has(permission)) {
42
- permissions.add(permission);
43
- }
44
- }
45
- }
46
- }
47
- this.permissions = permissions;
23
+ authToken.type === PrincipalType.OAuthAccess ? new Set<string>(authToken.permissions ?? []) : undefined;
24
+ this.permissions = new Set(
25
+ permissionCap ? rolePermissions.filter((permission) => permissionCap.has(permission)) : rolePermissions,
26
+ );
48
27
  }
49
28
 
50
29
  hasPermission(permission: string | string[]) {
@@ -64,6 +43,17 @@ export class UserPermissions {
64
43
  }
65
44
  }
66
45
 
46
+ function getPermissionsForRolesFromMappings(roleNames: Iterable<string>, roles: ListRolesResponse): Permission[] {
47
+ const permissionsByRole = new Map(roles.map((role) => [role.name, role.permissions]));
48
+ const permissions = new Set<Permission>();
49
+ for (const role of roleNames) {
50
+ for (const permission of permissionsByRole.get(role) ?? []) {
51
+ permissions.add(permission);
52
+ }
53
+ }
54
+ return Array.from(permissions);
55
+ }
56
+
67
57
  const UserPermissionsContext = createContext<UserPermissions | undefined>(undefined);
68
58
 
69
59
  export { UserPermissionsContext };
@@ -82,21 +72,28 @@ interface UserPermissionProviderProps {
82
72
  export function UserPermissionProvider({ children }: UserPermissionProviderProps) {
83
73
  const { t } = useUITranslation();
84
74
  const session = useUserSession();
75
+ const authToken = session.authToken;
76
+ const shouldFetchRoleMappings = Boolean(authToken && !authToken.permissions);
85
77
  const { data, error, isLoading } = useFetch<ListRolesResponse | undefined>(() => {
86
- if (session.user) {
78
+ if (shouldFetchRoleMappings) {
87
79
  return session.client.iam.roles.listSystem();
88
- } else {
89
- return Promise.resolve(undefined);
90
80
  }
91
- }, [session.user]);
81
+ return Promise.resolve(undefined);
82
+ }, [session, shouldFetchRoleMappings]);
92
83
 
93
84
  const perms = useMemo(() => {
94
- if (session.authToken && data && !isLoading) {
95
- return new UserPermissions(session, data);
85
+ if (authToken) {
86
+ if (authToken.permissions || !shouldFetchRoleMappings) {
87
+ return new UserPermissions(authToken);
88
+ }
89
+ if (data && !isLoading) {
90
+ return new UserPermissions(authToken, data);
91
+ }
96
92
  } else {
97
93
  return undefined;
98
94
  }
99
- }, [session, data, isLoading]);
95
+ return undefined;
96
+ }, [authToken, data, isLoading, shouldFetchRoleMappings]);
100
97
 
101
98
  if (error) {
102
99
  return <ErrorBox title={t('store.failedToFetchRoleMappings')}>{errorMessage(error)}</ErrorBox>;
@@ -25,12 +25,16 @@ import {
25
25
  import { useUITranslation } from '@vertesia/ui/i18n';
26
26
  import { NavLink } from '@vertesia/ui/router';
27
27
  import { useUserSession } from '@vertesia/ui/session';
28
- import { JSONDisplay, MarkdownRenderer, Progress, XMLViewer } from '@vertesia/ui/widgets';
28
+ import { JSONDisplay, Progress } from '@vertesia/ui/widgets';
29
29
  import { AlertTriangle, Copy, Download, FileSearch, SquarePen } from 'lucide-react';
30
- import { memo, type RefObject, useEffect, useRef, useState } from 'react';
30
+ import { memo, type RefObject, useEffect, useMemo, useRef, useState } from 'react';
31
+ import {
32
+ type UniversalDocumentConverter,
33
+ type UniversalDocumentSource,
34
+ UniversalDocumentViewer,
35
+ } from '../../../document-viewer/UniversalDocumentViewer.js';
31
36
  import { MagicPdfView } from '../../../magic-pdf';
32
37
  import { AudioPanel, ImagePanel, VideoPanel } from '../../../media-viewer';
33
- import { SimplePdfViewer } from '../../../pdf-viewer';
34
38
  import { SecureButton } from '../../../permissions/SecureButton.js';
35
39
  import { getWorkflowStatusColor, getWorkflowStatusName, isPreviewableAsPdf } from '../../../utils/index.js';
36
40
  import { PropertiesEditorModal } from './PropertiesEditorModal';
@@ -59,11 +63,12 @@ interface TextPanelProps {
59
63
  }
60
64
 
61
65
  interface OfficePdfPreviewPanelProps {
66
+ object: ContentObject;
62
67
  pdfRendition?: { content?: { source?: string } };
63
68
  officePdfUrl?: string;
69
+ converters: UniversalDocumentConverter[];
64
70
  officePdfConverting: boolean;
65
71
  officePdfError?: string;
66
- onConvert: () => void;
67
72
  }
68
73
 
69
74
  interface OfficePdfActionsProps {
@@ -320,6 +325,7 @@ function DataPanel({
320
325
  handleCopyContent: (content: string, type: 'text' | 'properties') => Promise<void>;
321
326
  refetch?: () => Promise<unknown>;
322
327
  }) {
328
+ const { client } = useUserSession();
323
329
  const { t } = useUITranslation();
324
330
  const isImage = object?.metadata?.type === ContentNature.Image;
325
331
  const isVideo = object?.metadata?.type === ContentNature.Video;
@@ -392,6 +398,38 @@ function DataPanel({
392
398
  triggerConversion: triggerOfficePdfConversion,
393
399
  } = useOfficePdfConversion(object.id, isPreviewableAsPdfDoc);
394
400
 
401
+ const officePdfConverters = useMemo<UniversalDocumentConverter[]>(
402
+ () => [
403
+ {
404
+ id: 'office-pdf-rendition',
405
+ target: 'pdf',
406
+ canConvert: ({ contentType, extension }) =>
407
+ isPreviewableAsPdfDoc ||
408
+ (contentType ? isPreviewableAsPdf(contentType) : false) ||
409
+ ['doc', 'docx', 'ppt', 'pptx'].includes(extension),
410
+ convert: async () => {
411
+ const response = await client.objects.getRendition(object.id, {
412
+ format: MarkdownRenditionFormat.pdf,
413
+ generate_if_missing: true,
414
+ sign_url: true,
415
+ block_on_generation: true,
416
+ });
417
+
418
+ if (response.status === 'found' && response.renditions?.length) {
419
+ return {
420
+ url: response.renditions[0],
421
+ contentType: 'application/pdf',
422
+ fileName: `${object.name || 'document'}.pdf`,
423
+ };
424
+ }
425
+
426
+ return null;
427
+ },
428
+ },
429
+ ],
430
+ [client, isPreviewableAsPdfDoc, object.id, object.name],
431
+ );
432
+
395
433
  // Load text once processing completes without triggering a full object refetch
396
434
  // (which would flash the page-level loading spinner).
397
435
  useEffect(() => {
@@ -539,11 +577,12 @@ function DataPanel({
539
577
  {isPreviewableAsPdfDoc && keepPdfPreviewMounted && (
540
578
  <div className={getPanelVisibility(showPdfPreviewPanel)}>
541
579
  <OfficePdfPreviewPanel
580
+ object={object}
542
581
  pdfRendition={pdfRendition}
543
582
  officePdfUrl={officePdfUrl}
583
+ converters={officePdfConverters}
544
584
  officePdfConverting={officePdfConverting}
545
585
  officePdfError={officePdfError}
546
- onConvert={triggerOfficePdfConversion}
547
586
  />
548
587
  </div>
549
588
  )}
@@ -743,18 +782,24 @@ function TextActions({ object, text, fullText, handleCopyContent, onToggleEdit,
743
782
 
744
783
  const TextPanel = memo(({ object, text, isTextCropped, textContainerRef }: TextPanelProps) => {
745
784
  const { t } = useUITranslation();
746
- const content = object.content;
747
785
  const isCreatedOrProcessing = isCreatedOrProcessingStatus(object?.status);
748
-
749
- // Check content processor type for XML
750
786
  const contentProcessorType = getContentProcessorType(object);
751
787
  const isXml = contentProcessorType === 'xml';
752
788
 
753
789
  // Check if content type is markdown or plain text
754
- const isMarkdownOrText = content?.type && (content.type === 'text/markdown' || content.type === 'text/plain');
790
+ const isMarkdownOrText =
791
+ object.content?.type && (object.content.type === 'text/markdown' || object.content.type === 'text/plain');
755
792
 
756
793
  // Render as markdown if it's markdown/text type OR if text looks like markdown (but not if XML)
757
794
  const shouldRenderAsMarkdown = !isXml && (isMarkdownOrText || looksLikeMarkdown(text));
795
+ const source: UniversalDocumentSource = {
796
+ id: object.id,
797
+ title: object.name,
798
+ fileName: object.content?.name || object.name,
799
+ contentType: isXml ? 'text/xml' : shouldRenderAsMarkdown ? 'text/markdown' : object.content?.type,
800
+ content: text,
801
+ sourcePath: object.content?.source,
802
+ };
758
803
 
759
804
  return text ? (
760
805
  <>
@@ -767,17 +812,12 @@ const TextPanel = memo(({ object, text, isTextCropped, textContainerRef }: TextP
767
812
  </div>
768
813
  )}
769
814
  <div className={`max-w-7xl px-2 h-full overflow-auto`} ref={textContainerRef}>
770
- {isXml ? (
771
- <div className="px-4 py-2">
772
- <XMLViewer xml={text} collapsible />
773
- </div>
774
- ) : shouldRenderAsMarkdown ? (
775
- <div className="vprose prose-sm p-1">
776
- <MarkdownRenderer components={createMarkdownComponents()}>{text}</MarkdownRenderer>
777
- </div>
778
- ) : (
779
- <pre className="text-wrap bg-muted text-muted p-2">{text}</pre>
780
- )}
815
+ <UniversalDocumentViewer
816
+ source={source}
817
+ className="h-full"
818
+ bodyClassName="overflow-auto"
819
+ markdownComponents={createMarkdownComponents()}
820
+ />
781
821
  </div>
782
822
  </>
783
823
  ) : (
@@ -921,9 +961,17 @@ function OfficePdfActions({ object, pdfRendition, officePdfUrl }: OfficePdfActio
921
961
  }
922
962
 
923
963
  function PdfPreviewPanel({ object }: { object: ContentObject }) {
964
+ const source: UniversalDocumentSource = {
965
+ id: object.id,
966
+ title: object.name,
967
+ fileName: object.content?.name || object.name,
968
+ contentType: object.content?.type,
969
+ sourcePath: object.content?.source,
970
+ };
971
+
924
972
  return (
925
973
  <div className="h-full">
926
- <SimplePdfViewer object={object} className="h-full" />
974
+ <UniversalDocumentViewer source={source} className="h-full" />
927
975
  </div>
928
976
  );
929
977
  }
@@ -933,11 +981,12 @@ function PdfPreviewPanel({ object }: { object: ContentObject }) {
933
981
  * Handles the various states: converting, error, showing PDF.
934
982
  */
935
983
  function OfficePdfPreviewPanel({
984
+ object,
936
985
  pdfRendition,
937
986
  officePdfUrl,
987
+ converters,
938
988
  officePdfConverting,
939
989
  officePdfError,
940
- onConvert,
941
990
  }: OfficePdfPreviewPanelProps) {
942
991
  const { t } = useUITranslation();
943
992
  if (officePdfConverting) {
@@ -959,24 +1008,48 @@ function OfficePdfPreviewPanel({
959
1008
  }
960
1009
 
961
1010
  if (pdfRendition?.content?.source) {
1011
+ const source: UniversalDocumentSource = {
1012
+ id: object.id,
1013
+ title: object.name,
1014
+ fileName: `${object.name || 'document'}.pdf`,
1015
+ contentType: 'application/pdf',
1016
+ sourcePath: pdfRendition.content.source,
1017
+ };
1018
+
962
1019
  return (
963
1020
  <div className="h-full">
964
- <SimplePdfViewer source={pdfRendition.content.source} className="h-full" />
1021
+ <UniversalDocumentViewer source={source} className="h-full" />
965
1022
  </div>
966
1023
  );
967
1024
  }
968
1025
 
969
1026
  if (officePdfUrl) {
1027
+ const source: UniversalDocumentSource = {
1028
+ id: object.id,
1029
+ title: object.name,
1030
+ fileName: `${object.name || 'document'}.pdf`,
1031
+ contentType: 'application/pdf',
1032
+ url: officePdfUrl,
1033
+ };
1034
+
970
1035
  return (
971
1036
  <div className="h-full">
972
- <SimplePdfViewer url={officePdfUrl} className="h-full" />
1037
+ <UniversalDocumentViewer source={source} className="h-full" />
973
1038
  </div>
974
1039
  );
975
1040
  }
976
1041
 
1042
+ const source: UniversalDocumentSource = {
1043
+ id: object.id,
1044
+ title: object.name,
1045
+ fileName: object.content?.name || object.name,
1046
+ contentType: object.content?.type,
1047
+ sourcePath: object.content?.source,
1048
+ };
1049
+
977
1050
  return (
978
- <div className="flex flex-col justify-center items-center flex-1 gap-2">
979
- <Button onClick={onConvert}>Convert to PDF</Button>
1051
+ <div className="h-full">
1052
+ <UniversalDocumentViewer source={source} converters={converters} className="h-full" />
980
1053
  </div>
981
1054
  );
982
1055
  }
@@ -128,6 +128,7 @@
128
128
  "agent.noPlanAvailable": "لا توجد خطة متاحة",
129
129
  "agent.noPlanDetected": "لم يتم اكتشاف أي خطة بعد",
130
130
  "agent.olderPlan": "خطة أقدم",
131
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
131
132
  "agent.openDocument": "فتح المستند",
132
133
  "agent.openInNewTab": "فتح في علامة تبويب جديدة",
133
134
  "agent.panelRenderError": "تعذر عرض {{name}}",
@@ -175,6 +176,7 @@
175
176
  "agent.showLess": "عرض أقل",
176
177
  "agent.showMore": "عرض المزيد",
177
178
  "agent.showSidebar": "عرض الشريط الجانبي",
179
+ "agent.showSystemArtifacts": "System files",
178
180
  "agent.staged": "مُعدّ",
179
181
  "agent.startAgent": "بدء الوكيل",
180
182
  "agent.startingAgent": "جارٍ بدء الوكيل...",
@@ -124,6 +124,7 @@
124
124
  "agent.noPlanAvailable": "Kein Plan verfügbar",
125
125
  "agent.noPlanDetected": "Noch kein Plan erkannt",
126
126
  "agent.olderPlan": "Älterer Plan",
127
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
127
128
  "agent.openDocument": "Dokument öffnen",
128
129
  "agent.openInNewTab": "In neuem Tab öffnen",
129
130
  "agent.panelRenderError": "{{name}} konnte nicht gerendert werden",
@@ -171,6 +172,7 @@
171
172
  "agent.showLess": "Weniger anzeigen",
172
173
  "agent.showMore": "Mehr anzeigen",
173
174
  "agent.showSidebar": "Seitenleiste anzeigen",
175
+ "agent.showSystemArtifacts": "System files",
174
176
  "agent.staged": "Bereitgestellt",
175
177
  "agent.startAgent": "Agent starten",
176
178
  "agent.startingAgent": "Agent wird gestartet...",
@@ -124,6 +124,7 @@
124
124
  "agent.noPlanAvailable": "No plan",
125
125
  "agent.noPlanDetected": "No plan has been detected yet",
126
126
  "agent.olderPlan": "Previous revision",
127
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
127
128
  "agent.openDocument": "Open document",
128
129
  "agent.openInNewTab": "Open in new tab",
129
130
  "agent.panelRenderError": "{{name}} could not be rendered",
@@ -171,6 +172,7 @@
171
172
  "agent.showLess": "Show less",
172
173
  "agent.showMore": "Show more",
173
174
  "agent.showSidebar": "Show Sidebar",
175
+ "agent.showSystemArtifacts": "System files",
174
176
  "agent.staged": "Staged",
175
177
  "agent.startAgent": "Start Agent",
176
178
  "agent.startingAgent": "Starting agent...",
@@ -125,6 +125,7 @@
125
125
  "agent.noPlanAvailable": "Sin plan disponible",
126
126
  "agent.noPlanDetected": "Aún no se ha detectado ningún plan",
127
127
  "agent.olderPlan": "Plan anterior",
128
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
128
129
  "agent.openDocument": "Abrir documento",
129
130
  "agent.openInNewTab": "Abrir en nueva pestaña",
130
131
  "agent.panelRenderError": "No se pudo renderizar {{name}}",
@@ -172,6 +173,7 @@
172
173
  "agent.showLess": "Mostrar menos",
173
174
  "agent.showMore": "Mostrar más",
174
175
  "agent.showSidebar": "Mostrar panel",
176
+ "agent.showSystemArtifacts": "System files",
175
177
  "agent.staged": "Preparado",
176
178
  "agent.startAgent": "Iniciar agente",
177
179
  "agent.startingAgent": "Iniciando agente...",
@@ -125,6 +125,7 @@
125
125
  "agent.noPlanAvailable": "Aucun plan disponible",
126
126
  "agent.noPlanDetected": "Aucun plan détecté pour le moment",
127
127
  "agent.olderPlan": "Plan précédent",
128
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
128
129
  "agent.openDocument": "Ouvrir le document",
129
130
  "agent.openInNewTab": "Ouvrir dans un nouvel onglet",
130
131
  "agent.panelRenderError": "{{name}} n'a pas pu être affiché",
@@ -172,6 +173,7 @@
172
173
  "agent.showLess": "Afficher moins",
173
174
  "agent.showMore": "Afficher plus",
174
175
  "agent.showSidebar": "Afficher le panneau",
176
+ "agent.showSystemArtifacts": "System files",
175
177
  "agent.staged": "En attente",
176
178
  "agent.startAgent": "Démarrer l'agent",
177
179
  "agent.startingAgent": "Démarrage de l'agent...",
@@ -125,6 +125,7 @@
125
125
  "agent.noPlanAvailable": "Nessun piano disponibile",
126
126
  "agent.noPlanDetected": "Nessun piano rilevato",
127
127
  "agent.olderPlan": "Piano precedente",
128
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
128
129
  "agent.openDocument": "Apri documento",
129
130
  "agent.openInNewTab": "Apri in nuova scheda",
130
131
  "agent.panelRenderError": "Impossibile renderizzare {{name}}",
@@ -172,6 +173,7 @@
172
173
  "agent.showLess": "Mostra meno",
173
174
  "agent.showMore": "Mostra altro",
174
175
  "agent.showSidebar": "Mostra pannello",
176
+ "agent.showSystemArtifacts": "System files",
175
177
  "agent.staged": "In attesa",
176
178
  "agent.startAgent": "Avvia agente",
177
179
  "agent.startingAgent": "Avvio dell'agente...",
@@ -124,6 +124,7 @@
124
124
  "agent.noPlanAvailable": "利用可能なプランなし",
125
125
  "agent.noPlanDetected": "まだプランが検出されていません",
126
126
  "agent.olderPlan": "古いプラン",
127
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
127
128
  "agent.openDocument": "ドキュメントを開く",
128
129
  "agent.openInNewTab": "新しいタブで開く",
129
130
  "agent.panelRenderError": "{{name}} を表示できませんでした",
@@ -171,6 +172,7 @@
171
172
  "agent.showLess": "表示を減らす",
172
173
  "agent.showMore": "さらに表示",
173
174
  "agent.showSidebar": "サイドバーを表示",
175
+ "agent.showSystemArtifacts": "System files",
174
176
  "agent.staged": "ステージ済み",
175
177
  "agent.startAgent": "エージェントを開始",
176
178
  "agent.startingAgent": "エージェントを起動中...",
@@ -124,6 +124,7 @@
124
124
  "agent.noPlanAvailable": "사용 가능한 계획 없음",
125
125
  "agent.noPlanDetected": "아직 계획이 감지되지 않았습니다",
126
126
  "agent.olderPlan": "이전 계획",
127
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
127
128
  "agent.openDocument": "문서 열기",
128
129
  "agent.openInNewTab": "새 탭에서 열기",
129
130
  "agent.panelRenderError": "{{name}}을(를) 렌더링할 수 없습니다",
@@ -171,6 +172,7 @@
171
172
  "agent.showLess": "덜 보기",
172
173
  "agent.showMore": "더 보기",
173
174
  "agent.showSidebar": "사이드바 표시",
175
+ "agent.showSystemArtifacts": "System files",
174
176
  "agent.staged": "준비됨",
175
177
  "agent.startAgent": "에이전트 시작",
176
178
  "agent.startingAgent": "에이전트 시작 중...",
@@ -125,6 +125,7 @@
125
125
  "agent.noPlanAvailable": "Sem plano disponível",
126
126
  "agent.noPlanDetected": "Nenhum plano detectado ainda",
127
127
  "agent.olderPlan": "Plano anterior",
128
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
128
129
  "agent.openDocument": "Abrir documento",
129
130
  "agent.openInNewTab": "Abrir em nova aba",
130
131
  "agent.panelRenderError": "Não foi possível renderizar {{name}}",
@@ -172,6 +173,7 @@
172
173
  "agent.showLess": "Mostrar menos",
173
174
  "agent.showMore": "Mostrar mais",
174
175
  "agent.showSidebar": "Mostrar painel",
176
+ "agent.showSystemArtifacts": "System files",
175
177
  "agent.staged": "Preparado",
176
178
  "agent.startAgent": "Iniciar agente",
177
179
  "agent.startingAgent": "Iniciando agente...",
@@ -126,6 +126,7 @@
126
126
  "agent.noPlanAvailable": "План недоступен",
127
127
  "agent.noPlanDetected": "План пока не обнаружен",
128
128
  "agent.olderPlan": "Более старый план",
129
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
129
130
  "agent.openDocument": "Открыть документ",
130
131
  "agent.openInNewTab": "Открыть в новой вкладке",
131
132
  "agent.panelRenderError": "Не удалось отобразить {{name}}",
@@ -173,6 +174,7 @@
173
174
  "agent.showLess": "Показать меньше",
174
175
  "agent.showMore": "Показать больше",
175
176
  "agent.showSidebar": "Показать боковую панель",
177
+ "agent.showSystemArtifacts": "System files",
176
178
  "agent.staged": "Подготовлено",
177
179
  "agent.startAgent": "Запустить агента",
178
180
  "agent.startingAgent": "Запуск агента...",
@@ -124,6 +124,7 @@
124
124
  "agent.noPlanAvailable": "Plan mevcut değil",
125
125
  "agent.noPlanDetected": "Henüz bir plan algılanmadı",
126
126
  "agent.olderPlan": "Daha eski plan",
127
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
127
128
  "agent.openDocument": "Belgeyi aç",
128
129
  "agent.openInNewTab": "Yeni sekmede aç",
129
130
  "agent.panelRenderError": "{{name}} işlenemedi",
@@ -171,6 +172,7 @@
171
172
  "agent.showLess": "Daha az göster",
172
173
  "agent.showMore": "Daha fazla göster",
173
174
  "agent.showSidebar": "Kenar çubuğunu göster",
175
+ "agent.showSystemArtifacts": "System files",
174
176
  "agent.staged": "Hazırlandı",
175
177
  "agent.startAgent": "Ajanı başlat",
176
178
  "agent.startingAgent": "Ajan başlatılıyor...",
@@ -124,6 +124,7 @@
124
124
  "agent.noPlanAvailable": "暫無計劃",
125
125
  "agent.noPlanDetected": "尚未偵測到計劃",
126
126
  "agent.olderPlan": "較舊的計劃",
127
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
127
128
  "agent.openDocument": "開啟文件",
128
129
  "agent.openInNewTab": "在新分頁中開啟",
129
130
  "agent.panelRenderError": "無法呈現 {{name}}",
@@ -171,6 +172,7 @@
171
172
  "agent.showLess": "顯示較少",
172
173
  "agent.showMore": "顯示更多",
173
174
  "agent.showSidebar": "顯示側邊欄",
175
+ "agent.showSystemArtifacts": "System files",
174
176
  "agent.staged": "已暫存",
175
177
  "agent.startAgent": "啟動代理",
176
178
  "agent.startingAgent": "正在啟動代理...",
@@ -124,6 +124,7 @@
124
124
  "agent.noPlanAvailable": "暂无计划",
125
125
  "agent.noPlanDetected": "尚未检测到计划",
126
126
  "agent.olderPlan": "较旧的计划",
127
+ "agent.onlySystemArtifacts": "Only system files in this run. Enable \"System files\" to view them.",
127
128
  "agent.openDocument": "打开文档",
128
129
  "agent.openInNewTab": "在新标签页中打开",
129
130
  "agent.panelRenderError": "无法渲染 {{name}}",
@@ -171,6 +172,7 @@
171
172
  "agent.showLess": "显示更少",
172
173
  "agent.showMore": "显示更多",
173
174
  "agent.showSidebar": "显示侧边栏",
175
+ "agent.showSystemArtifacts": "System files",
174
176
  "agent.staged": "已暂存",
175
177
  "agent.startAgent": "启动代理",
176
178
  "agent.startingAgent": "正在启动代理...",
@@ -1,5 +1,5 @@
1
1
  import { PathWithParams } from './PathWithParams';
2
- import { joinPath } from './path';
2
+ import { joinPath, withMountBasename } from './path';
3
3
 
4
4
  const BASE_PATH = Symbol('BASE_PATH');
5
5
 
@@ -149,6 +149,9 @@ export class HistoryNavigator {
149
149
  to = joinPath(basePath, to);
150
150
  }
151
151
  to = this.addStickyParams(to);
152
+ // Keep absolute app routes under the served `<base href>` mount instead of resolving them
153
+ // against the bare origin (which collapses the URL off the mount). No-op when origin-served.
154
+ to = withMountBasename(to);
152
155
  this._navigate(new URL(to, window.location.href), 'navigate', options);
153
156
  }
154
157
 
@@ -1,4 +1,5 @@
1
1
  import type { SyntheticEvent } from 'react';
2
+ import { withMountBasename } from './path';
2
3
  import { useNavigate, useRouterContext } from './Router';
3
4
 
4
5
  /**
@@ -75,7 +76,12 @@ export function NavLink({
75
76
  const isAnchorOrEmpty = !href || href.startsWith('#');
76
77
  const isExternal = /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//') || (!!target && target !== '_self');
77
78
  const isInternal = !isAnchorOrEmpty && !isExternal;
78
- const resolvedHref = !skipStickyParams && isInternal ? router.getTopRouter().navigator.addStickyParams(href) : href;
79
+ // Keep the rendered href under the served `<base href>` mount (correct middle-click / hover /
80
+ // open-in-new-tab); the onClick navigates via the router which applies the same rule. No-op when
81
+ // origin-served (Studio UI). Click handler below passes the raw `href` — navigate() re-bases it.
82
+ const resolvedHref = isInternal
83
+ ? withMountBasename(!skipStickyParams ? router.getTopRouter().navigator.addStickyParams(href) : href)
84
+ : href;
79
85
  const _onClick = (ev: SyntheticEvent) => {
80
86
  if (ev.defaultPrevented || !isInternal) {
81
87
  return;