@red-hat-developer-hub/backstage-plugin-intelligent-assistant 5.1.0 → 5.3.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +40 -5
  3. package/dist/alpha.d.ts +61 -0
  4. package/dist/api/LightspeedApiClient.esm.js +57 -0
  5. package/dist/api/LightspeedApiClient.esm.js.map +1 -1
  6. package/dist/api/api.esm.js.map +1 -1
  7. package/dist/components/DeleteModal.esm.js +2 -0
  8. package/dist/components/DeleteModal.esm.js.map +1 -1
  9. package/dist/components/DeleteSavedPromptModal.esm.js +75 -0
  10. package/dist/components/DeleteSavedPromptModal.esm.js.map +1 -0
  11. package/dist/components/LightSpeedChat.esm.js +276 -134
  12. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  13. package/dist/components/LightspeedChatBoxHeader.esm.js +57 -2
  14. package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
  15. package/dist/components/LightspeedChatContainer.esm.js +2 -1
  16. package/dist/components/LightspeedChatContainer.esm.js.map +1 -1
  17. package/dist/components/LightspeedDrawerContext.esm.js.map +1 -1
  18. package/dist/components/LightspeedDrawerProvider.esm.js +1 -0
  19. package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -1
  20. package/dist/components/McpServersSettings.esm.js +9 -22
  21. package/dist/components/McpServersSettings.esm.js.map +1 -1
  22. package/dist/components/MessageBarModelSelector.esm.js +104 -12
  23. package/dist/components/MessageBarModelSelector.esm.js.map +1 -1
  24. package/dist/components/SavedPromptCard.esm.js +100 -0
  25. package/dist/components/SavedPromptCard.esm.js.map +1 -0
  26. package/dist/components/SavedPromptKebabMenu.esm.js +87 -0
  27. package/dist/components/SavedPromptKebabMenu.esm.js.map +1 -0
  28. package/dist/components/SavedPromptMenuItems.esm.js +52 -0
  29. package/dist/components/SavedPromptMenuItems.esm.js.map +1 -0
  30. package/dist/components/SavedPromptsEmptyState.esm.js +63 -0
  31. package/dist/components/SavedPromptsEmptyState.esm.js.map +1 -0
  32. package/dist/components/SavedPromptsList.esm.js +63 -0
  33. package/dist/components/SavedPromptsList.esm.js.map +1 -0
  34. package/dist/components/SavedPromptsSettings.esm.js +285 -0
  35. package/dist/components/SavedPromptsSettings.esm.js.map +1 -0
  36. package/dist/components/ScreenContextChip.esm.js +172 -0
  37. package/dist/components/ScreenContextChip.esm.js.map +1 -0
  38. package/dist/components/SettingsPanel.esm.js +135 -0
  39. package/dist/components/SettingsPanel.esm.js.map +1 -0
  40. package/dist/hooks/screenContextLabelSubscription.esm.js +113 -0
  41. package/dist/hooks/screenContextLabelSubscription.esm.js.map +1 -0
  42. package/dist/hooks/useConversationHistoryGroups.esm.js +71 -0
  43. package/dist/hooks/useConversationHistoryGroups.esm.js.map +1 -0
  44. package/dist/hooks/useFeedbackActions.esm.js.map +1 -1
  45. package/dist/hooks/useLightspeedProviderState.esm.js +19 -5
  46. package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -1
  47. package/dist/hooks/useSavedPromptActions.esm.js +64 -0
  48. package/dist/hooks/useSavedPromptActions.esm.js.map +1 -0
  49. package/dist/hooks/useSavedPrompts.esm.js +75 -0
  50. package/dist/hooks/useSavedPrompts.esm.js.map +1 -0
  51. package/dist/hooks/useSavedPromptsSettings.esm.js +65 -0
  52. package/dist/hooks/useSavedPromptsSettings.esm.js.map +1 -0
  53. package/dist/hooks/useScreenContextLabelRevision.esm.js +13 -0
  54. package/dist/hooks/useScreenContextLabelRevision.esm.js.map +1 -0
  55. package/dist/hooks/useScreenContextSettings.esm.js +110 -0
  56. package/dist/hooks/useScreenContextSettings.esm.js.map +1 -0
  57. package/dist/hooks/useSettingsPanelUrlState.esm.js +95 -0
  58. package/dist/hooks/useSettingsPanelUrlState.esm.js.map +1 -0
  59. package/dist/hooks/useWelcomePrompts.esm.js +16 -5
  60. package/dist/hooks/useWelcomePrompts.esm.js.map +1 -1
  61. package/dist/hooks/useWindowLocation.esm.js +28 -0
  62. package/dist/hooks/useWindowLocation.esm.js.map +1 -0
  63. package/dist/translations/de.esm.js +64 -1
  64. package/dist/translations/de.esm.js.map +1 -1
  65. package/dist/translations/es.esm.js +64 -1
  66. package/dist/translations/es.esm.js.map +1 -1
  67. package/dist/translations/fr.esm.js +64 -1
  68. package/dist/translations/fr.esm.js.map +1 -1
  69. package/dist/translations/it.esm.js +64 -1
  70. package/dist/translations/it.esm.js.map +1 -1
  71. package/dist/translations/ja.esm.js +64 -1
  72. package/dist/translations/ja.esm.js.map +1 -1
  73. package/dist/translations/ref.esm.js +64 -2
  74. package/dist/translations/ref.esm.js.map +1 -1
  75. package/dist/utils/buildConversationHistoryGroups.esm.js +241 -0
  76. package/dist/utils/buildConversationHistoryGroups.esm.js.map +1 -0
  77. package/dist/utils/dom-selectors.esm.js +22 -2
  78. package/dist/utils/dom-selectors.esm.js.map +1 -1
  79. package/dist/utils/prompt-utils.esm.js +18 -1
  80. package/dist/utils/prompt-utils.esm.js.map +1 -1
  81. package/dist/utils/screen-capture.esm.js +116 -0
  82. package/dist/utils/screen-capture.esm.js.map +1 -0
  83. package/dist/utils/screen-context-utils.esm.js +343 -0
  84. package/dist/utils/screen-context-utils.esm.js.map +1 -0
  85. package/dist/utils/sensitive-data-redactor.esm.js +39 -1
  86. package/dist/utils/sensitive-data-redactor.esm.js.map +1 -1
  87. package/package.json +5 -15
@@ -0,0 +1,75 @@
1
+ import { useCallback } from 'react';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
4
+ import { lightspeedApiRef } from '../api/api.esm.js';
5
+
6
+ const DEFAULT_LIMITS = {
7
+ max_prompts_per_user: 100,
8
+ max_display_name_length: 128,
9
+ max_content_length: 5e3
10
+ };
11
+ const useSavedPrompts = () => {
12
+ const api = useApi(lightspeedApiRef);
13
+ const queryClient = useQueryClient();
14
+ const configQuery = useQuery({
15
+ queryKey: ["savedPromptsConfig"],
16
+ queryFn: async () => {
17
+ try {
18
+ return await api.getSavedPromptsConfig();
19
+ } catch {
20
+ return DEFAULT_LIMITS;
21
+ }
22
+ },
23
+ staleTime: Infinity
24
+ });
25
+ const promptsQuery = useQuery({
26
+ queryKey: ["savedPrompts"],
27
+ queryFn: () => api.getSavedPrompts(),
28
+ staleTime: 1e3 * 60 * 5
29
+ });
30
+ const createMutation = useMutation({
31
+ mutationFn: ({ name, content }) => api.createSavedPrompt({ name, content }),
32
+ onSuccess: async () => {
33
+ await queryClient.invalidateQueries({ queryKey: ["savedPrompts"] });
34
+ }
35
+ });
36
+ const deleteMutation = useMutation({
37
+ mutationFn: (promptId) => api.deleteSavedPrompt(promptId),
38
+ onSuccess: async () => {
39
+ await queryClient.invalidateQueries({ queryKey: ["savedPrompts"] });
40
+ }
41
+ });
42
+ const createPrompt = useCallback(
43
+ async (name, content) => {
44
+ await createMutation.mutateAsync({ name, content });
45
+ },
46
+ [createMutation]
47
+ );
48
+ const deletePrompt = useCallback(
49
+ async (promptId) => {
50
+ await deleteMutation.mutateAsync(promptId);
51
+ },
52
+ [deleteMutation]
53
+ );
54
+ const refresh = useCallback(() => {
55
+ void queryClient.invalidateQueries({ queryKey: ["savedPrompts"] });
56
+ }, [queryClient]);
57
+ let error = null;
58
+ if (promptsQuery.error instanceof Error) {
59
+ error = promptsQuery.error.message;
60
+ } else if (promptsQuery.error) {
61
+ error = "Failed to fetch saved prompts";
62
+ }
63
+ return {
64
+ savedPrompts: promptsQuery.data ?? [],
65
+ config: configQuery.data ?? DEFAULT_LIMITS,
66
+ loading: configQuery.isLoading || promptsQuery.isLoading,
67
+ error,
68
+ createPrompt,
69
+ deletePrompt,
70
+ refresh
71
+ };
72
+ };
73
+
74
+ export { useSavedPrompts };
75
+ //# sourceMappingURL=useSavedPrompts.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSavedPrompts.esm.js","sources":["../../src/hooks/useSavedPrompts.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback } from 'react';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport {\n SavedPrompt,\n SavedPromptsConfig,\n} from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';\n\nimport { lightspeedApiRef } from '../api/api';\n\nconst DEFAULT_LIMITS: SavedPromptsConfig = {\n max_prompts_per_user: 100,\n max_display_name_length: 128,\n max_content_length: 5000,\n};\n\ntype UseSavedPromptsReturn = {\n savedPrompts: SavedPrompt[];\n config: SavedPromptsConfig;\n loading: boolean;\n error: string | null;\n createPrompt: (name: string, content: string) => Promise<void>;\n deletePrompt: (promptId: string) => Promise<void>;\n refresh: () => void;\n};\n\n/**\n * Hook to manage saved prompts CRUD operations and configuration.\n * Fetches config once per session (cached in state with DEFAULT_LIMITS fallback).\n */\nexport const useSavedPrompts = (): UseSavedPromptsReturn => {\n const api = useApi(lightspeedApiRef);\n const queryClient = useQueryClient();\n\n const configQuery = useQuery({\n queryKey: ['savedPromptsConfig'],\n queryFn: async () => {\n try {\n return await api.getSavedPromptsConfig();\n } catch {\n return DEFAULT_LIMITS;\n }\n },\n staleTime: Infinity,\n });\n\n const promptsQuery = useQuery({\n queryKey: ['savedPrompts'],\n queryFn: () => api.getSavedPrompts(),\n staleTime: 1000 * 60 * 5,\n });\n\n const createMutation = useMutation({\n mutationFn: ({ name, content }: { name: string; content: string }) =>\n api.createSavedPrompt({ name, content }),\n onSuccess: async () => {\n await queryClient.invalidateQueries({ queryKey: ['savedPrompts'] });\n },\n });\n\n const deleteMutation = useMutation({\n mutationFn: (promptId: string) => api.deleteSavedPrompt(promptId),\n onSuccess: async () => {\n await queryClient.invalidateQueries({ queryKey: ['savedPrompts'] });\n },\n });\n\n const createPrompt = useCallback(\n async (name: string, content: string) => {\n await createMutation.mutateAsync({ name, content });\n },\n [createMutation],\n );\n\n const deletePrompt = useCallback(\n async (promptId: string) => {\n await deleteMutation.mutateAsync(promptId);\n },\n [deleteMutation],\n );\n\n const refresh = useCallback(() => {\n void queryClient.invalidateQueries({ queryKey: ['savedPrompts'] });\n }, [queryClient]);\n\n let error: string | null = null;\n if (promptsQuery.error instanceof Error) {\n error = promptsQuery.error.message;\n } else if (promptsQuery.error) {\n error = 'Failed to fetch saved prompts';\n }\n\n return {\n savedPrompts: promptsQuery.data ?? [],\n config: configQuery.data ?? DEFAULT_LIMITS,\n loading: configQuery.isLoading || promptsQuery.isLoading,\n error,\n createPrompt,\n deletePrompt,\n refresh,\n };\n};\n"],"names":[],"mappings":";;;;;AA4BA,MAAM,cAAA,GAAqC;AAAA,EACzC,oBAAA,EAAsB,GAAA;AAAA,EACtB,uBAAA,EAAyB,GAAA;AAAA,EACzB,kBAAA,EAAoB;AACtB,CAAA;AAgBO,MAAM,kBAAkB,MAA6B;AAC1D,EAAA,MAAM,GAAA,GAAM,OAAO,gBAAgB,CAAA;AACnC,EAAA,MAAM,cAAc,cAAA,EAAe;AAEnC,EAAA,MAAM,cAAc,QAAA,CAAS;AAAA,IAC3B,QAAA,EAAU,CAAC,oBAAoB,CAAA;AAAA,IAC/B,SAAS,YAAY;AACnB,MAAA,IAAI;AACF,QAAA,OAAO,MAAM,IAAI,qBAAA,EAAsB;AAAA,MACzC,CAAA,CAAA,MAAQ;AACN,QAAA,OAAO,cAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IACA,SAAA,EAAW;AAAA,GACZ,CAAA;AAED,EAAA,MAAM,eAAe,QAAA,CAAS;AAAA,IAC5B,QAAA,EAAU,CAAC,cAAc,CAAA;AAAA,IACzB,OAAA,EAAS,MAAM,GAAA,CAAI,eAAA,EAAgB;AAAA,IACnC,SAAA,EAAW,MAAO,EAAA,GAAK;AAAA,GACxB,CAAA;AAED,EAAA,MAAM,iBAAiB,WAAA,CAAY;AAAA,IACjC,UAAA,EAAY,CAAC,EAAE,IAAA,EAAM,OAAA,EAAQ,KAC3B,GAAA,CAAI,iBAAA,CAAkB,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AAAA,IACzC,WAAW,YAAY;AACrB,MAAA,MAAM,YAAY,iBAAA,CAAkB,EAAE,UAAU,CAAC,cAAc,GAAG,CAAA;AAAA,IACpE;AAAA,GACD,CAAA;AAED,EAAA,MAAM,iBAAiB,WAAA,CAAY;AAAA,IACjC,UAAA,EAAY,CAAC,QAAA,KAAqB,GAAA,CAAI,kBAAkB,QAAQ,CAAA;AAAA,IAChE,WAAW,YAAY;AACrB,MAAA,MAAM,YAAY,iBAAA,CAAkB,EAAE,UAAU,CAAC,cAAc,GAAG,CAAA;AAAA,IACpE;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAe,WAAA;AAAA,IACnB,OAAO,MAAc,OAAA,KAAoB;AACvC,MAAA,MAAM,cAAA,CAAe,WAAA,CAAY,EAAE,IAAA,EAAM,SAAS,CAAA;AAAA,IACpD,CAAA;AAAA,IACA,CAAC,cAAc;AAAA,GACjB;AAEA,EAAA,MAAM,YAAA,GAAe,WAAA;AAAA,IACnB,OAAO,QAAA,KAAqB;AAC1B,MAAA,MAAM,cAAA,CAAe,YAAY,QAAQ,CAAA;AAAA,IAC3C,CAAA;AAAA,IACA,CAAC,cAAc;AAAA,GACjB;AAEA,EAAA,MAAM,OAAA,GAAU,YAAY,MAAM;AAChC,IAAA,KAAK,YAAY,iBAAA,CAAkB,EAAE,UAAU,CAAC,cAAc,GAAG,CAAA;AAAA,EACnE,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAA,IAAI,KAAA,GAAuB,IAAA;AAC3B,EAAA,IAAI,YAAA,CAAa,iBAAiB,KAAA,EAAO;AACvC,IAAA,KAAA,GAAQ,aAAa,KAAA,CAAM,OAAA;AAAA,EAC7B,CAAA,MAAA,IAAW,aAAa,KAAA,EAAO;AAC7B,IAAA,KAAA,GAAQ,+BAAA;AAAA,EACV;AAEA,EAAA,OAAO;AAAA,IACL,YAAA,EAAc,YAAA,CAAa,IAAA,IAAQ,EAAC;AAAA,IACpC,MAAA,EAAQ,YAAY,IAAA,IAAQ,cAAA;AAAA,IAC5B,OAAA,EAAS,WAAA,CAAY,SAAA,IAAa,YAAA,CAAa,SAAA;AAAA,IAC/C,KAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,65 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { useApi, storageApiRef } from '@backstage/core-plugin-api';
3
+ import { isGuestUser } from '../utils/user-utils.esm.js';
4
+
5
+ const BUCKET_NAME = "lightspeed";
6
+ const SAVED_PROMPTS_ENABLED_KEY = "savedPromptsEnabled";
7
+ const useSavedPromptsSettings = (user) => {
8
+ const storageApi = useApi(storageApiRef);
9
+ const bucket = storageApi.forBucket(BUCKET_NAME);
10
+ const [isSavedPromptsEnabled, setIsSavedPromptsEnabled] = useState(true);
11
+ const shouldPersist = !isGuestUser(user);
12
+ useEffect(() => {
13
+ if (!user) {
14
+ setIsSavedPromptsEnabled(true);
15
+ return void 0;
16
+ }
17
+ if (isGuestUser(user)) {
18
+ setIsSavedPromptsEnabled(true);
19
+ return void 0;
20
+ }
21
+ try {
22
+ const enabledSnapshot = bucket.snapshot(
23
+ SAVED_PROMPTS_ENABLED_KEY
24
+ );
25
+ setIsSavedPromptsEnabled(enabledSnapshot.value ?? true);
26
+ } catch (error) {
27
+ console.error(
28
+ "Error reading saved prompts settings from storage:",
29
+ error
30
+ );
31
+ }
32
+ const enabledSubscription = bucket.observe$(SAVED_PROMPTS_ENABLED_KEY).subscribe({
33
+ next: (snapshot) => {
34
+ setIsSavedPromptsEnabled(snapshot.value ?? true);
35
+ },
36
+ error: (error) => {
37
+ console.error("Error observing savedPromptsEnabled:", error);
38
+ }
39
+ });
40
+ return () => {
41
+ enabledSubscription.unsubscribe();
42
+ };
43
+ }, [bucket, user]);
44
+ const handleSavedPromptsToggle = useCallback(
45
+ (enabled) => {
46
+ if (!user) return;
47
+ setIsSavedPromptsEnabled(enabled);
48
+ if (shouldPersist) {
49
+ try {
50
+ bucket.set(SAVED_PROMPTS_ENABLED_KEY, enabled);
51
+ } catch (error) {
52
+ console.error("Error saving saved prompts toggle state:", error);
53
+ }
54
+ }
55
+ },
56
+ [bucket, user, shouldPersist]
57
+ );
58
+ return {
59
+ isSavedPromptsEnabled,
60
+ handleSavedPromptsToggle
61
+ };
62
+ };
63
+
64
+ export { useSavedPromptsSettings };
65
+ //# sourceMappingURL=useSavedPromptsSettings.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSavedPromptsSettings.esm.js","sources":["../../src/hooks/useSavedPromptsSettings.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback, useEffect, useState } from 'react';\n\nimport { storageApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { isGuestUser } from '../utils/user-utils';\n\nconst BUCKET_NAME = 'lightspeed';\nconst SAVED_PROMPTS_ENABLED_KEY = 'savedPromptsEnabled';\n\ntype UseSavedPromptsSettingsReturn = {\n isSavedPromptsEnabled: boolean;\n handleSavedPromptsToggle: (enabled: boolean) => void;\n};\n\n/**\n * Hook to manage the saved prompts enable/disable preference with persistence\n * using Backstage StorageApi. Follows the same pattern as usePinnedChatsSettings.\n *\n * @param user - The user entity ref (e.g., \"user:default/john\")\n */\nexport const useSavedPromptsSettings = (\n user: string | undefined,\n): UseSavedPromptsSettingsReturn => {\n const storageApi = useApi(storageApiRef);\n const bucket = storageApi.forBucket(BUCKET_NAME);\n\n const [isSavedPromptsEnabled, setIsSavedPromptsEnabled] = useState(true);\n\n const shouldPersist = !isGuestUser(user);\n\n useEffect(() => {\n if (!user) {\n setIsSavedPromptsEnabled(true);\n return undefined;\n }\n\n if (isGuestUser(user)) {\n setIsSavedPromptsEnabled(true);\n return undefined;\n }\n\n try {\n const enabledSnapshot = bucket.snapshot<boolean>(\n SAVED_PROMPTS_ENABLED_KEY,\n );\n setIsSavedPromptsEnabled(enabledSnapshot.value ?? true);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\n 'Error reading saved prompts settings from storage:',\n error,\n );\n }\n\n const enabledSubscription = bucket\n .observe$<boolean>(SAVED_PROMPTS_ENABLED_KEY)\n .subscribe({\n next: snapshot => {\n setIsSavedPromptsEnabled(snapshot.value ?? true);\n },\n error: error => {\n // eslint-disable-next-line no-console\n console.error('Error observing savedPromptsEnabled:', error);\n },\n });\n\n return () => {\n enabledSubscription.unsubscribe();\n };\n }, [bucket, user]);\n\n const handleSavedPromptsToggle = useCallback(\n (enabled: boolean) => {\n if (!user) return;\n\n setIsSavedPromptsEnabled(enabled);\n\n if (shouldPersist) {\n try {\n bucket.set(SAVED_PROMPTS_ENABLED_KEY, enabled);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Error saving saved prompts toggle state:', error);\n }\n }\n },\n [bucket, user, shouldPersist],\n );\n\n return {\n isSavedPromptsEnabled,\n handleSavedPromptsToggle,\n };\n};\n"],"names":[],"mappings":";;;;AAqBA,MAAM,WAAA,GAAc,YAAA;AACpB,MAAM,yBAAA,GAA4B,qBAAA;AAa3B,MAAM,uBAAA,GAA0B,CACrC,IAAA,KACkC;AAClC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,MAAA,GAAS,UAAA,CAAW,SAAA,CAAU,WAAW,CAAA;AAE/C,EAAA,MAAM,CAAC,qBAAA,EAAuB,wBAAwB,CAAA,GAAI,SAAS,IAAI,CAAA;AAEvE,EAAA,MAAM,aAAA,GAAgB,CAAC,WAAA,CAAY,IAAI,CAAA;AAEvC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,wBAAA,CAAyB,IAAI,CAAA;AAC7B,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,WAAA,CAAY,IAAI,CAAA,EAAG;AACrB,MAAA,wBAAA,CAAyB,IAAI,CAAA;AAC7B,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,kBAAkB,MAAA,CAAO,QAAA;AAAA,QAC7B;AAAA,OACF;AACA,MAAA,wBAAA,CAAyB,eAAA,CAAgB,SAAS,IAAI,CAAA;AAAA,IACxD,SAAS,KAAA,EAAO;AAEd,MAAA,OAAA,CAAQ,KAAA;AAAA,QACN,oDAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,mBAAA,GAAsB,MAAA,CACzB,QAAA,CAAkB,yBAAyB,EAC3C,SAAA,CAAU;AAAA,MACT,MAAM,CAAA,QAAA,KAAY;AAChB,QAAA,wBAAA,CAAyB,QAAA,CAAS,SAAS,IAAI,CAAA;AAAA,MACjD,CAAA;AAAA,MACA,OAAO,CAAA,KAAA,KAAS;AAEd,QAAA,OAAA,CAAQ,KAAA,CAAM,wCAAwC,KAAK,CAAA;AAAA,MAC7D;AAAA,KACD,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,mBAAA,CAAoB,WAAA,EAAY;AAAA,IAClC,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,IAAI,CAAC,CAAA;AAEjB,EAAA,MAAM,wBAAA,GAA2B,WAAA;AAAA,IAC/B,CAAC,OAAA,KAAqB;AACpB,MAAA,IAAI,CAAC,IAAA,EAAM;AAEX,MAAA,wBAAA,CAAyB,OAAO,CAAA;AAEhC,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,IAAI;AACF,UAAA,MAAA,CAAO,GAAA,CAAI,2BAA2B,OAAO,CAAA;AAAA,QAC/C,SAAS,KAAA,EAAO;AAEd,UAAA,OAAA,CAAQ,KAAA,CAAM,4CAA4C,KAAK,CAAA;AAAA,QACjE;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,IAAA,EAAM,aAAa;AAAA,GAC9B;AAEA,EAAA,OAAO;AAAA,IACL,qBAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,13 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ import { subscribeToScreenContextLabel, getScreenContextLabelSnapshot } from './screenContextLabelSubscription.esm.js';
3
+
4
+ function useScreenContextLabelRevision() {
5
+ return useSyncExternalStore(
6
+ subscribeToScreenContextLabel,
7
+ getScreenContextLabelSnapshot,
8
+ getScreenContextLabelSnapshot
9
+ );
10
+ }
11
+
12
+ export { useScreenContextLabelRevision };
13
+ //# sourceMappingURL=useScreenContextLabelRevision.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScreenContextLabelRevision.esm.js","sources":["../../src/hooks/useScreenContextLabelRevision.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useSyncExternalStore } from 'react';\n\nimport {\n getScreenContextLabelSnapshot,\n subscribeToScreenContextLabel,\n} from './screenContextLabelSubscription';\n\n/**\n * Re-renders when the browser URL or main page chrome title changes\n * (history navigation, async header updates after route transitions).\n */\nexport function useScreenContextLabelRevision(): string {\n return useSyncExternalStore(\n subscribeToScreenContextLabel,\n getScreenContextLabelSnapshot,\n getScreenContextLabelSnapshot,\n );\n}\n"],"names":[],"mappings":";;;AA2BO,SAAS,6BAAA,GAAwC;AACtD,EAAA,OAAO,oBAAA;AAAA,IACL,6BAAA;AAAA,IACA,6BAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,110 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { useApi, storageApiRef } from '@backstage/core-plugin-api';
3
+ import { isGuestUser } from '../utils/user-utils.esm.js';
4
+
5
+ const BUCKET_NAME = "lightspeed";
6
+ const SHARING_ENABLED_KEY = "screenContextSharingEnabled";
7
+ const PAUSED_KEY = "screenContextPaused";
8
+ const useScreenContextSettings = (user) => {
9
+ const storageApi = useApi(storageApiRef);
10
+ const bucket = storageApi.forBucket(BUCKET_NAME);
11
+ const [isScreenContextSharingEnabled, setIsScreenContextSharingEnabled] = useState(false);
12
+ const [isScreenContextPaused, setIsScreenContextPaused] = useState(false);
13
+ const shouldPersist = Boolean(user) && !isGuestUser(user);
14
+ useEffect(() => {
15
+ if (!user) {
16
+ setIsScreenContextSharingEnabled(false);
17
+ setIsScreenContextPaused(false);
18
+ return void 0;
19
+ }
20
+ if (isGuestUser(user)) {
21
+ setIsScreenContextSharingEnabled(false);
22
+ setIsScreenContextPaused(false);
23
+ return void 0;
24
+ }
25
+ try {
26
+ const sharingSnapshot = bucket.snapshot(SHARING_ENABLED_KEY);
27
+ const pausedSnapshot = bucket.snapshot(PAUSED_KEY);
28
+ setIsScreenContextSharingEnabled(sharingSnapshot.value ?? false);
29
+ setIsScreenContextPaused(pausedSnapshot.value ?? false);
30
+ } catch (error) {
31
+ console.error(
32
+ "Error reading screen context settings from storage:",
33
+ error
34
+ );
35
+ }
36
+ const sharingSubscription = bucket.observe$(SHARING_ENABLED_KEY).subscribe({
37
+ next: (snapshot) => {
38
+ setIsScreenContextSharingEnabled(snapshot.value ?? false);
39
+ },
40
+ error: (error) => {
41
+ console.error("Error observing screenContextSharingEnabled:", error);
42
+ }
43
+ });
44
+ const pausedSubscription = bucket.observe$(PAUSED_KEY).subscribe({
45
+ next: (snapshot) => {
46
+ setIsScreenContextPaused(snapshot.value ?? false);
47
+ },
48
+ error: (error) => {
49
+ console.error("Error observing screenContextPaused:", error);
50
+ }
51
+ });
52
+ return () => {
53
+ sharingSubscription.unsubscribe();
54
+ pausedSubscription.unsubscribe();
55
+ };
56
+ }, [bucket, user]);
57
+ const persistSharing = useCallback(
58
+ (enabled) => {
59
+ if (!shouldPersist) return;
60
+ try {
61
+ bucket.set(SHARING_ENABLED_KEY, enabled);
62
+ } catch (error) {
63
+ console.error("Error saving screen context sharing state:", error);
64
+ }
65
+ },
66
+ [bucket, shouldPersist]
67
+ );
68
+ const persistPaused = useCallback(
69
+ (paused) => {
70
+ if (!shouldPersist) return;
71
+ try {
72
+ bucket.set(PAUSED_KEY, paused);
73
+ } catch (error) {
74
+ console.error("Error saving screen context paused state:", error);
75
+ }
76
+ },
77
+ [bucket, shouldPersist]
78
+ );
79
+ const handleScreenContextSharingToggle = useCallback(
80
+ (enabled) => {
81
+ if (!user) return;
82
+ setIsScreenContextSharingEnabled(enabled);
83
+ setIsScreenContextPaused(false);
84
+ persistPaused(false);
85
+ persistSharing(enabled);
86
+ },
87
+ [user, persistSharing, persistPaused]
88
+ );
89
+ const setScreenContextPaused = useCallback(
90
+ (paused) => {
91
+ if (!user) return;
92
+ setIsScreenContextPaused(paused);
93
+ persistPaused(paused);
94
+ },
95
+ [user, persistPaused]
96
+ );
97
+ const toggleScreenContextPaused = useCallback(() => {
98
+ setScreenContextPaused(!isScreenContextPaused);
99
+ }, [isScreenContextPaused, setScreenContextPaused]);
100
+ return {
101
+ isScreenContextSharingEnabled,
102
+ isScreenContextPaused,
103
+ handleScreenContextSharingToggle,
104
+ toggleScreenContextPaused,
105
+ setScreenContextPaused
106
+ };
107
+ };
108
+
109
+ export { useScreenContextSettings };
110
+ //# sourceMappingURL=useScreenContextSettings.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScreenContextSettings.esm.js","sources":["../../src/hooks/useScreenContextSettings.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback, useEffect, useState } from 'react';\n\nimport { storageApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { isGuestUser } from '../utils/user-utils';\n\nconst BUCKET_NAME = 'lightspeed';\nconst SHARING_ENABLED_KEY = 'screenContextSharingEnabled';\nconst PAUSED_KEY = 'screenContextPaused';\n\ntype UseScreenContextSettingsReturn = {\n isScreenContextSharingEnabled: boolean;\n isScreenContextPaused: boolean;\n handleScreenContextSharingToggle: (enabled: boolean) => void;\n toggleScreenContextPaused: () => void;\n setScreenContextPaused: (paused: boolean) => void;\n};\n\n/**\n * Persists screen context user preference (opt-in sharing + pause state).\n */\nexport const useScreenContextSettings = (\n user: string | undefined,\n): UseScreenContextSettingsReturn => {\n const storageApi = useApi(storageApiRef);\n const bucket = storageApi.forBucket(BUCKET_NAME);\n\n const [isScreenContextSharingEnabled, setIsScreenContextSharingEnabled] =\n useState(false);\n const [isScreenContextPaused, setIsScreenContextPaused] = useState(false);\n\n const shouldPersist = Boolean(user) && !isGuestUser(user);\n\n useEffect(() => {\n if (!user) {\n setIsScreenContextSharingEnabled(false);\n setIsScreenContextPaused(false);\n return undefined;\n }\n\n if (isGuestUser(user)) {\n setIsScreenContextSharingEnabled(false);\n setIsScreenContextPaused(false);\n return undefined;\n }\n\n try {\n const sharingSnapshot = bucket.snapshot<boolean>(SHARING_ENABLED_KEY);\n const pausedSnapshot = bucket.snapshot<boolean>(PAUSED_KEY);\n setIsScreenContextSharingEnabled(sharingSnapshot.value ?? false);\n setIsScreenContextPaused(pausedSnapshot.value ?? false);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\n 'Error reading screen context settings from storage:',\n error,\n );\n }\n\n const sharingSubscription = bucket\n .observe$<boolean>(SHARING_ENABLED_KEY)\n .subscribe({\n next: snapshot => {\n setIsScreenContextSharingEnabled(snapshot.value ?? false);\n },\n error: error => {\n // eslint-disable-next-line no-console\n console.error('Error observing screenContextSharingEnabled:', error);\n },\n });\n\n const pausedSubscription = bucket.observe$<boolean>(PAUSED_KEY).subscribe({\n next: snapshot => {\n setIsScreenContextPaused(snapshot.value ?? false);\n },\n error: error => {\n // eslint-disable-next-line no-console\n console.error('Error observing screenContextPaused:', error);\n },\n });\n\n return () => {\n sharingSubscription.unsubscribe();\n pausedSubscription.unsubscribe();\n };\n }, [bucket, user]);\n\n const persistSharing = useCallback(\n (enabled: boolean) => {\n if (!shouldPersist) return;\n try {\n bucket.set(SHARING_ENABLED_KEY, enabled);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Error saving screen context sharing state:', error);\n }\n },\n [bucket, shouldPersist],\n );\n\n const persistPaused = useCallback(\n (paused: boolean) => {\n if (!shouldPersist) return;\n try {\n bucket.set(PAUSED_KEY, paused);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Error saving screen context paused state:', error);\n }\n },\n [bucket, shouldPersist],\n );\n\n const handleScreenContextSharingToggle = useCallback(\n (enabled: boolean) => {\n if (!user) return;\n\n setIsScreenContextSharingEnabled(enabled);\n setIsScreenContextPaused(false);\n persistPaused(false);\n persistSharing(enabled);\n },\n [user, persistSharing, persistPaused],\n );\n\n const setScreenContextPaused = useCallback(\n (paused: boolean) => {\n if (!user) return;\n setIsScreenContextPaused(paused);\n persistPaused(paused);\n },\n [user, persistPaused],\n );\n\n const toggleScreenContextPaused = useCallback(() => {\n setScreenContextPaused(!isScreenContextPaused);\n }, [isScreenContextPaused, setScreenContextPaused]);\n\n return {\n isScreenContextSharingEnabled,\n isScreenContextPaused,\n handleScreenContextSharingToggle,\n toggleScreenContextPaused,\n setScreenContextPaused,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { useCallback, useEffect } from 'react';
2
+ import { useLocation, useSearchParams } from 'react-router-dom';
3
+ import { LIGHTSPEED_PATH } from '../const.esm.js';
4
+
5
+ const SETTINGS_TAB_PARAM = "settingsTab";
6
+ const VALID_SETTINGS_TABS = /* @__PURE__ */ new Set([
7
+ "mcp-servers",
8
+ "saved-prompts"
9
+ ]);
10
+ const parseSettingsTabParam = (value) => {
11
+ if (!value) {
12
+ return null;
13
+ }
14
+ return VALID_SETTINGS_TABS.has(value) ? value : null;
15
+ };
16
+ const useSettingsPanelUrlState = (isFullscreenMode, persistedSettingsTab, setPersistedSettingsTab) => {
17
+ const location = useLocation();
18
+ const [searchParams, setSearchParams] = useSearchParams();
19
+ const shouldSyncUrl = isFullscreenMode && location.pathname.startsWith(LIGHTSPEED_PATH);
20
+ const settingsTabFromUrl = parseSettingsTabParam(
21
+ searchParams.get(SETTINGS_TAB_PARAM)
22
+ );
23
+ const isOpen = shouldSyncUrl ? settingsTabFromUrl !== null : persistedSettingsTab !== null;
24
+ const activeTab = shouldSyncUrl ? settingsTabFromUrl ?? "mcp-servers" : persistedSettingsTab ?? "mcp-servers";
25
+ const updateSearchParam = useCallback(
26
+ (tab) => {
27
+ const nextParams = new URLSearchParams(searchParams);
28
+ if (tab) {
29
+ nextParams.set(SETTINGS_TAB_PARAM, tab);
30
+ } else {
31
+ nextParams.delete(SETTINGS_TAB_PARAM);
32
+ }
33
+ setSearchParams(nextParams, { replace: true });
34
+ },
35
+ [searchParams, setSearchParams]
36
+ );
37
+ useEffect(() => {
38
+ if (!shouldSyncUrl) {
39
+ return;
40
+ }
41
+ if (settingsTabFromUrl !== persistedSettingsTab) {
42
+ setPersistedSettingsTab(settingsTabFromUrl);
43
+ }
44
+ }, [
45
+ shouldSyncUrl,
46
+ settingsTabFromUrl,
47
+ persistedSettingsTab,
48
+ setPersistedSettingsTab
49
+ ]);
50
+ useEffect(() => {
51
+ if (!shouldSyncUrl || !persistedSettingsTab || settingsTabFromUrl) {
52
+ return;
53
+ }
54
+ updateSearchParam(persistedSettingsTab);
55
+ }, [
56
+ shouldSyncUrl,
57
+ persistedSettingsTab,
58
+ settingsTabFromUrl,
59
+ updateSearchParam
60
+ ]);
61
+ const openSettings = useCallback(
62
+ (tab) => {
63
+ setPersistedSettingsTab(tab);
64
+ if (shouldSyncUrl) {
65
+ updateSearchParam(tab);
66
+ }
67
+ },
68
+ [shouldSyncUrl, setPersistedSettingsTab, updateSearchParam]
69
+ );
70
+ const closeSettings = useCallback(() => {
71
+ setPersistedSettingsTab(null);
72
+ if (shouldSyncUrl) {
73
+ updateSearchParam(null);
74
+ }
75
+ }, [shouldSyncUrl, setPersistedSettingsTab, updateSearchParam]);
76
+ const setActiveTab = useCallback(
77
+ (tab) => {
78
+ setPersistedSettingsTab(tab);
79
+ if (shouldSyncUrl) {
80
+ updateSearchParam(tab);
81
+ }
82
+ },
83
+ [shouldSyncUrl, setPersistedSettingsTab, updateSearchParam]
84
+ );
85
+ return {
86
+ isOpen,
87
+ activeTab,
88
+ openSettings,
89
+ closeSettings,
90
+ setActiveTab
91
+ };
92
+ };
93
+
94
+ export { SETTINGS_TAB_PARAM, parseSettingsTabParam, useSettingsPanelUrlState };
95
+ //# sourceMappingURL=useSettingsPanelUrlState.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSettingsPanelUrlState.esm.js","sources":["../../src/hooks/useSettingsPanelUrlState.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useEffect } from 'react';\nimport { useLocation, useSearchParams } from 'react-router-dom';\n\nimport { LIGHTSPEED_PATH } from '../const';\n\nexport type SettingsTab = 'mcp-servers' | 'saved-prompts';\n\nexport const SETTINGS_TAB_PARAM = 'settingsTab';\n\nconst VALID_SETTINGS_TABS = new Set<SettingsTab>([\n 'mcp-servers',\n 'saved-prompts',\n]);\n\nexport const parseSettingsTabParam = (\n value: string | null,\n): SettingsTab | null => {\n if (!value) {\n return null;\n }\n return VALID_SETTINGS_TABS.has(value as SettingsTab)\n ? (value as SettingsTab)\n : null;\n};\n\nexport const useSettingsPanelUrlState = (\n isFullscreenMode: boolean,\n persistedSettingsTab: SettingsTab | null,\n setPersistedSettingsTab: (tab: SettingsTab | null) => void,\n) => {\n const location = useLocation();\n const [searchParams, setSearchParams] = useSearchParams();\n\n const shouldSyncUrl =\n isFullscreenMode && location.pathname.startsWith(LIGHTSPEED_PATH);\n\n const settingsTabFromUrl = parseSettingsTabParam(\n searchParams.get(SETTINGS_TAB_PARAM),\n );\n\n const isOpen = shouldSyncUrl\n ? settingsTabFromUrl !== null\n : persistedSettingsTab !== null;\n const activeTab = shouldSyncUrl\n ? (settingsTabFromUrl ?? 'mcp-servers')\n : (persistedSettingsTab ?? 'mcp-servers');\n\n const updateSearchParam = useCallback(\n (tab: SettingsTab | null) => {\n const nextParams = new URLSearchParams(searchParams);\n if (tab) {\n nextParams.set(SETTINGS_TAB_PARAM, tab);\n } else {\n nextParams.delete(SETTINGS_TAB_PARAM);\n }\n setSearchParams(nextParams, { replace: true });\n },\n [searchParams, setSearchParams],\n );\n\n useEffect(() => {\n if (!shouldSyncUrl) {\n return;\n }\n if (settingsTabFromUrl !== persistedSettingsTab) {\n setPersistedSettingsTab(settingsTabFromUrl);\n }\n }, [\n shouldSyncUrl,\n settingsTabFromUrl,\n persistedSettingsTab,\n setPersistedSettingsTab,\n ]);\n\n useEffect(() => {\n if (!shouldSyncUrl || !persistedSettingsTab || settingsTabFromUrl) {\n return;\n }\n updateSearchParam(persistedSettingsTab);\n }, [\n shouldSyncUrl,\n persistedSettingsTab,\n settingsTabFromUrl,\n updateSearchParam,\n ]);\n\n const openSettings = useCallback(\n (tab: SettingsTab) => {\n setPersistedSettingsTab(tab);\n if (shouldSyncUrl) {\n updateSearchParam(tab);\n }\n },\n [shouldSyncUrl, setPersistedSettingsTab, updateSearchParam],\n );\n\n const closeSettings = useCallback(() => {\n setPersistedSettingsTab(null);\n if (shouldSyncUrl) {\n updateSearchParam(null);\n }\n }, [shouldSyncUrl, setPersistedSettingsTab, updateSearchParam]);\n\n const setActiveTab = useCallback(\n (tab: SettingsTab) => {\n setPersistedSettingsTab(tab);\n if (shouldSyncUrl) {\n updateSearchParam(tab);\n }\n },\n [shouldSyncUrl, setPersistedSettingsTab, updateSearchParam],\n );\n\n return {\n isOpen,\n activeTab,\n openSettings,\n closeSettings,\n setActiveTab,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { useMemo } from 'react';
2
2
  import { useApi, configApiRef } from '@backstage/core-plugin-api';
3
3
  import { RHDH_SAMPLE_PROMPTS, DEFAULT_SAMPLE_PROMPTS } from '../const.esm.js';
4
- import { getRandomSamplePrompts } from '../utils/prompt-utils.esm.js';
4
+ import { getPriorityBasedPrompts, getRandomSamplePrompts } from '../utils/prompt-utils.esm.js';
5
5
  import { useTopicRestrictionStatus } from './useQuestionValidation.esm.js';
6
6
  import { useTranslation } from './useTranslation.esm.js';
7
7
 
8
- const useWelcomePrompts = (enabled = true) => {
8
+ const useWelcomePrompts = (enabled = true, savedPrompts) => {
9
9
  const configApi = useApi(configApiRef);
10
10
  const { t } = useTranslation();
11
11
  const { data: questionValidationEnabled } = useTopicRestrictionStatus(enabled);
@@ -23,13 +23,24 @@ const useWelcomePrompts = (enabled = true) => {
23
23
  return prompt;
24
24
  });
25
25
  };
26
- const DEFAULT_PROMPTS = questionValidationEnabled ? translatePrompts(RHDH_SAMPLE_PROMPTS) : translatePrompts([...DEFAULT_SAMPLE_PROMPTS, ...RHDH_SAMPLE_PROMPTS]);
26
+ const hardcodedDefaults = questionValidationEnabled ? translatePrompts(RHDH_SAMPLE_PROMPTS) : translatePrompts([...DEFAULT_SAMPLE_PROMPTS, ...RHDH_SAMPLE_PROMPTS]);
27
27
  const userConfiguredPrompts = (configApi?.getOptionalConfigArray("intelligent-assistant.prompts") ?? []).map((config) => ({
28
28
  title: config.getString("title") ?? "",
29
29
  message: config.getString("message") ?? ""
30
30
  }));
31
- return getRandomSamplePrompts(userConfiguredPrompts, DEFAULT_PROMPTS);
32
- }, [configApi, t, questionValidationEnabled]);
31
+ if (savedPrompts && savedPrompts.length > 0) {
32
+ const savedAsSample = savedPrompts.map((p) => ({
33
+ title: p.name,
34
+ message: p.content
35
+ }));
36
+ return getPriorityBasedPrompts(
37
+ userConfiguredPrompts,
38
+ savedAsSample,
39
+ hardcodedDefaults
40
+ );
41
+ }
42
+ return getRandomSamplePrompts(userConfiguredPrompts, hardcodedDefaults);
43
+ }, [configApi, t, questionValidationEnabled, savedPrompts]);
33
44
  };
34
45
 
35
46
  export { useWelcomePrompts };
@@ -1 +1 @@
1
- {"version":3,"file":"useWelcomePrompts.esm.js","sources":["../../src/hooks/useWelcomePrompts.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useMemo } from 'react';\n\nimport { ConfigApi, configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { DEFAULT_SAMPLE_PROMPTS, RHDH_SAMPLE_PROMPTS } from '../const';\nimport { SamplePrompt, SamplePrompts } from '../types';\nimport { getRandomSamplePrompts } from '../utils/prompt-utils';\nimport { useTopicRestrictionStatus } from './useQuestionValidation';\nimport { useTranslation } from './useTranslation';\n\nexport const useWelcomePrompts = (enabled = true): SamplePrompts => {\n const configApi: ConfigApi = useApi(configApiRef);\n const { t } = useTranslation();\n const { data: questionValidationEnabled } =\n useTopicRestrictionStatus(enabled);\n\n return useMemo(() => {\n // Transform translation keys to actual prompts\n const translatePrompts = (prompts: SamplePrompts): SamplePrompts => {\n return prompts.map((prompt: SamplePrompt) => {\n if ('titleKey' in prompt && 'messageKey' in prompt) {\n return {\n title: t(prompt.titleKey as any, { defaultValue: prompt.titleKey }),\n message: t(prompt.messageKey as any, {\n defaultValue: prompt.messageKey,\n }),\n };\n }\n return prompt as { title: string; message: string };\n });\n };\n\n const DEFAULT_PROMPTS = questionValidationEnabled\n ? translatePrompts(RHDH_SAMPLE_PROMPTS)\n : translatePrompts([...DEFAULT_SAMPLE_PROMPTS, ...RHDH_SAMPLE_PROMPTS]);\n\n const userConfiguredPrompts: SamplePrompts = (\n configApi?.getOptionalConfigArray('intelligent-assistant.prompts') ?? []\n ).map(config => ({\n title: config.getString('title') ?? '',\n message: config.getString('message') ?? '',\n }));\n return getRandomSamplePrompts(userConfiguredPrompts, DEFAULT_PROMPTS);\n }, [configApi, t, questionValidationEnabled]);\n};\n"],"names":[],"mappings":";;;;;;;AA0BO,MAAM,iBAAA,GAAoB,CAAC,OAAA,GAAU,IAAA,KAAwB;AAClE,EAAA,MAAM,SAAA,GAAuB,OAAO,YAAY,CAAA;AAChD,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,cAAA,EAAe;AAC7B,EAAA,MAAM,EAAE,IAAA,EAAM,yBAAA,EAA0B,GACtC,0BAA0B,OAAO,CAAA;AAEnC,EAAA,OAAO,QAAQ,MAAM;AAEnB,IAAA,MAAM,gBAAA,GAAmB,CAAC,OAAA,KAA0C;AAClE,MAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAyB;AAC3C,QAAA,IAAI,UAAA,IAAc,MAAA,IAAU,YAAA,IAAgB,MAAA,EAAQ;AAClD,UAAA,OAAO;AAAA,YACL,KAAA,EAAO,EAAE,MAAA,CAAO,QAAA,EAAiB,EAAE,YAAA,EAAc,MAAA,CAAO,UAAU,CAAA;AAAA,YAClE,OAAA,EAAS,CAAA,CAAE,MAAA,CAAO,UAAA,EAAmB;AAAA,cACnC,cAAc,MAAA,CAAO;AAAA,aACtB;AAAA,WACH;AAAA,QACF;AACA,QAAA,OAAO,MAAA;AAAA,MACT,CAAC,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,MAAM,eAAA,GAAkB,yBAAA,GACpB,gBAAA,CAAiB,mBAAmB,CAAA,GACpC,gBAAA,CAAiB,CAAC,GAAG,sBAAA,EAAwB,GAAG,mBAAmB,CAAC,CAAA;AAExE,IAAA,MAAM,qBAAA,GAAA,CACJ,WAAW,sBAAA,CAAuB,+BAA+B,KAAK,EAAC,EACvE,IAAI,CAAA,MAAA,MAAW;AAAA,MACf,KAAA,EAAO,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA,IAAK,EAAA;AAAA,MACpC,OAAA,EAAS,MAAA,CAAO,SAAA,CAAU,SAAS,CAAA,IAAK;AAAA,KAC1C,CAAE,CAAA;AACF,IAAA,OAAO,sBAAA,CAAuB,uBAAuB,eAAe,CAAA;AAAA,EACtE,CAAA,EAAG,CAAC,SAAA,EAAW,CAAA,EAAG,yBAAyB,CAAC,CAAA;AAC9C;;;;"}
1
+ {"version":3,"file":"useWelcomePrompts.esm.js","sources":["../../src/hooks/useWelcomePrompts.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useMemo } from 'react';\n\nimport { ConfigApi, configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { SavedPrompt } from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';\n\nimport { DEFAULT_SAMPLE_PROMPTS, RHDH_SAMPLE_PROMPTS } from '../const';\nimport { SamplePrompt, SamplePrompts } from '../types';\nimport {\n getPriorityBasedPrompts,\n getRandomSamplePrompts,\n} from '../utils/prompt-utils';\nimport { useTopicRestrictionStatus } from './useQuestionValidation';\nimport { useTranslation } from './useTranslation';\n\nexport const useWelcomePrompts = (\n enabled = true,\n savedPrompts?: SavedPrompt[],\n): SamplePrompts => {\n const configApi: ConfigApi = useApi(configApiRef);\n const { t } = useTranslation();\n const { data: questionValidationEnabled } =\n useTopicRestrictionStatus(enabled);\n\n return useMemo(() => {\n const translatePrompts = (prompts: SamplePrompts): SamplePrompts => {\n return prompts.map((prompt: SamplePrompt) => {\n if ('titleKey' in prompt && 'messageKey' in prompt) {\n return {\n title: t(prompt.titleKey as any, { defaultValue: prompt.titleKey }),\n message: t(prompt.messageKey as any, {\n defaultValue: prompt.messageKey,\n }),\n };\n }\n return prompt as { title: string; message: string };\n });\n };\n\n const hardcodedDefaults = questionValidationEnabled\n ? translatePrompts(RHDH_SAMPLE_PROMPTS)\n : translatePrompts([...DEFAULT_SAMPLE_PROMPTS, ...RHDH_SAMPLE_PROMPTS]);\n\n const userConfiguredPrompts: SamplePrompts = (\n configApi?.getOptionalConfigArray('intelligent-assistant.prompts') ?? []\n ).map(config => ({\n title: config.getString('title') ?? '',\n message: config.getString('message') ?? '',\n }));\n\n if (savedPrompts && savedPrompts.length > 0) {\n const savedAsSample: SamplePrompts = savedPrompts.map(p => ({\n title: p.name,\n message: p.content,\n }));\n return getPriorityBasedPrompts(\n userConfiguredPrompts,\n savedAsSample,\n hardcodedDefaults,\n );\n }\n\n return getRandomSamplePrompts(userConfiguredPrompts, hardcodedDefaults);\n }, [configApi, t, questionValidationEnabled, savedPrompts]);\n};\n"],"names":[],"mappings":";;;;;;;AA+BO,MAAM,iBAAA,GAAoB,CAC/B,OAAA,GAAU,IAAA,EACV,YAAA,KACkB;AAClB,EAAA,MAAM,SAAA,GAAuB,OAAO,YAAY,CAAA;AAChD,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,cAAA,EAAe;AAC7B,EAAA,MAAM,EAAE,IAAA,EAAM,yBAAA,EAA0B,GACtC,0BAA0B,OAAO,CAAA;AAEnC,EAAA,OAAO,QAAQ,MAAM;AACnB,IAAA,MAAM,gBAAA,GAAmB,CAAC,OAAA,KAA0C;AAClE,MAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAyB;AAC3C,QAAA,IAAI,UAAA,IAAc,MAAA,IAAU,YAAA,IAAgB,MAAA,EAAQ;AAClD,UAAA,OAAO;AAAA,YACL,KAAA,EAAO,EAAE,MAAA,CAAO,QAAA,EAAiB,EAAE,YAAA,EAAc,MAAA,CAAO,UAAU,CAAA;AAAA,YAClE,OAAA,EAAS,CAAA,CAAE,MAAA,CAAO,UAAA,EAAmB;AAAA,cACnC,cAAc,MAAA,CAAO;AAAA,aACtB;AAAA,WACH;AAAA,QACF;AACA,QAAA,OAAO,MAAA;AAAA,MACT,CAAC,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,MAAM,iBAAA,GAAoB,yBAAA,GACtB,gBAAA,CAAiB,mBAAmB,CAAA,GACpC,gBAAA,CAAiB,CAAC,GAAG,sBAAA,EAAwB,GAAG,mBAAmB,CAAC,CAAA;AAExE,IAAA,MAAM,qBAAA,GAAA,CACJ,WAAW,sBAAA,CAAuB,+BAA+B,KAAK,EAAC,EACvE,IAAI,CAAA,MAAA,MAAW;AAAA,MACf,KAAA,EAAO,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA,IAAK,EAAA;AAAA,MACpC,OAAA,EAAS,MAAA,CAAO,SAAA,CAAU,SAAS,CAAA,IAAK;AAAA,KAC1C,CAAE,CAAA;AAEF,IAAA,IAAI,YAAA,IAAgB,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG;AAC3C,MAAA,MAAM,aAAA,GAA+B,YAAA,CAAa,GAAA,CAAI,CAAA,CAAA,MAAM;AAAA,QAC1D,OAAO,CAAA,CAAE,IAAA;AAAA,QACT,SAAS,CAAA,CAAE;AAAA,OACb,CAAE,CAAA;AACF,MAAA,OAAO,uBAAA;AAAA,QACL,qBAAA;AAAA,QACA,aAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AAEA,IAAA,OAAO,sBAAA,CAAuB,uBAAuB,iBAAiB,CAAA;AAAA,EACxE,GAAG,CAAC,SAAA,EAAW,CAAA,EAAG,yBAAA,EAA2B,YAAY,CAAC,CAAA;AAC5D;;;;"}
@@ -0,0 +1,28 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ import { subscribeToScreenContextLabel } from './screenContextLabelSubscription.esm.js';
3
+
4
+ let cachedSnapshot = {
5
+ pathname: "",
6
+ search: ""
7
+ };
8
+ function getWindowLocationSnapshot() {
9
+ const pathname = window.location.pathname;
10
+ const search = window.location.search;
11
+ if (cachedSnapshot.pathname !== pathname || cachedSnapshot.search !== search) {
12
+ cachedSnapshot = { pathname, search };
13
+ }
14
+ return cachedSnapshot;
15
+ }
16
+ function subscribeToWindowLocation(onStoreChange) {
17
+ return subscribeToScreenContextLabel(onStoreChange);
18
+ }
19
+ function useWindowLocation() {
20
+ return useSyncExternalStore(
21
+ subscribeToWindowLocation,
22
+ getWindowLocationSnapshot,
23
+ getWindowLocationSnapshot
24
+ );
25
+ }
26
+
27
+ export { useWindowLocation };
28
+ //# sourceMappingURL=useWindowLocation.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWindowLocation.esm.js","sources":["../../src/hooks/useWindowLocation.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useSyncExternalStore } from 'react';\n\nimport { subscribeToScreenContextLabel } from './screenContextLabelSubscription';\n\nexport type WindowLocationSnapshot = {\n pathname: string;\n search: string;\n};\n\nlet cachedSnapshot: WindowLocationSnapshot = {\n pathname: '',\n search: '',\n};\n\nfunction getWindowLocationSnapshot(): WindowLocationSnapshot {\n const pathname = window.location.pathname;\n const search = window.location.search;\n if (\n cachedSnapshot.pathname !== pathname ||\n cachedSnapshot.search !== search\n ) {\n cachedSnapshot = { pathname, search };\n }\n return cachedSnapshot;\n}\n\nfunction subscribeToWindowLocation(onStoreChange: () => void): () => void {\n return subscribeToScreenContextLabel(onStoreChange);\n}\n\n/**\n * Subscribes to browser URL changes (including history.pushState/replaceState).\n * Use when the host app navigates outside the same React Router tree as Lightspeed.\n * History patching is installed on first subscribe via subscribeToScreenContextLabel.\n */\nexport function useWindowLocation(): WindowLocationSnapshot {\n return useSyncExternalStore(\n subscribeToWindowLocation,\n getWindowLocationSnapshot,\n getWindowLocationSnapshot,\n );\n}\n"],"names":[],"mappings":";;;AAyBA,IAAI,cAAA,GAAyC;AAAA,EAC3C,QAAA,EAAU,EAAA;AAAA,EACV,MAAA,EAAQ;AACV,CAAA;AAEA,SAAS,yBAAA,GAAoD;AAC3D,EAAA,MAAM,QAAA,GAAW,OAAO,QAAA,CAAS,QAAA;AACjC,EAAA,MAAM,MAAA,GAAS,OAAO,QAAA,CAAS,MAAA;AAC/B,EAAA,IACE,cAAA,CAAe,QAAA,KAAa,QAAA,IAC5B,cAAA,CAAe,WAAW,MAAA,EAC1B;AACA,IAAA,cAAA,GAAiB,EAAE,UAAU,MAAA,EAAO;AAAA,EACtC;AACA,EAAA,OAAO,cAAA;AACT;AAEA,SAAS,0BAA0B,aAAA,EAAuC;AACxE,EAAA,OAAO,8BAA8B,aAAa,CAAA;AACpD;AAOO,SAAS,iBAAA,GAA4C;AAC1D,EAAA,OAAO,oBAAA;AAAA,IACL,yBAAA;AAAA,IACA,yBAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -5,6 +5,9 @@ const intelligentAssistantTranslationDe = createTranslationMessages({
5
5
  ref: intelligentAssistantTranslationRef,
6
6
  messages: {
7
7
  "aria.chatHistoryMenu": "Chatverlauf-Men\xFC",
8
+ "modelSelector.visionScreenshot.line1": "Bildanalyse wird unterst\xFCtzt.",
9
+ "modelSelector.visionScreenshot.line2": "Ein Screenshot wird Ihrer Nachricht beigef\xFCgt.",
10
+ "modelSelector.visionScreenshot.ariaLabel": "Screenshot-Kontext f\xFCr Vision-Modell",
8
11
  "modelSelector.disabledTooltip": "Jede Chatsitzung unterst\xFCtzt nur ein Modell. Um das Modell zu wechseln, starten Sie einen neuen Chat.",
9
12
  "aria.chatbotSelector": "Chatbot-Auswahl",
10
13
  "aria.close": "Chatbot schlie\xDFen",
@@ -42,6 +45,7 @@ const intelligentAssistantTranslationDe = createTranslationMessages({
42
45
  "conversation.announcement.userMessage": "Nachricht vom Benutzer: {{prompt}}. Nachricht vom Bot wird geladen.",
43
46
  "conversation.category.pinnedChats": "Angeheftete Chats",
44
47
  "conversation.category.recent": "Chats",
48
+ "conversation.category.savedPrompts": "Gespeicherte Prompts",
45
49
  "conversation.delete": "L\xF6schen",
46
50
  "conversation.delete.confirm.action": "L\xF6schen",
47
51
  "conversation.delete.confirm.message": "Dieser Chat wird hier nicht mehr angezeigt. Dadurch werden auch zugeh\xF6rige Aktivit\xE4ten wie Prompts, Antworten und Feedback aus Ihrer Aktivit\xE4t gel\xF6scht.",
@@ -180,6 +184,7 @@ const intelligentAssistantTranslationDe = createTranslationMessages({
180
184
  "notebook.view.documents.count_other": "{{count}} Ressourcen",
181
185
  "notebook.view.documents.maxReached": "Maximal 10 Ressourcen sind erlaubt. L\xF6schen Sie eine Ressource, um eine neue hochzuladen.",
182
186
  "notebook.view.documents.uploading": "Ressource wird hochgeladen",
187
+ "notebook.view.documents.uploadsInProgress": "Bitte warten Sie, bis die aktuellen Uploads abgeschlossen sind, bevor Sie weitere Ressourcen hinzuf\xFCgen.",
183
188
  "notebook.view.input.disabledTooltip": "W\xE4hlen Sie mindestens eine geladene Ressource aus, um den Chat zu starten",
184
189
  "notebook.view.input.placeholder": "Fragen Sie zu Ihren Ressourcen...",
185
190
  "notebook.view.sidebar.collapse": "Seitenleiste einklappen",
@@ -244,11 +249,68 @@ const intelligentAssistantTranslationDe = createTranslationMessages({
244
249
  "settings.displayMode.label": "Anzeigemodus",
245
250
  "settings.displayMode.overlay": "Overlay",
246
251
  "settings.mcp.badge": "Neu",
247
- "settings.mcp.label": "MCP-Einstellungen",
252
+ "settings.panel.title": "Einstellungen",
253
+ "settings.mcp.label": "MCP- und Prompt-Einstellungen",
254
+ "settings.prompt.label": "Prompt-Einstellungen",
248
255
  "settings.pinned.disable": "Angeheftete Chats deaktivieren",
249
256
  "settings.pinned.disabled.description": "Angeheftete Chats sind derzeit deaktiviert.",
250
257
  "settings.pinned.enable": "Angeheftete Chats aktivieren",
251
258
  "settings.pinned.enabled.description": "Angeheftete Chats sind derzeit aktiviert",
259
+ "settings.savedPrompts.disable": "Gespeicherte Prompts deaktivieren",
260
+ "settings.savedPrompts.disabled.description": "Gespeicherte Prompts sind derzeit deaktiviert",
261
+ "settings.savedPrompts.enable": "Gespeicherte Prompts aktivieren",
262
+ "settings.savedPrompts.enabled.description": "Gespeicherte Prompts sind derzeit aktiviert",
263
+ "settings.screenContext.enable": "Bildschirmkontext aktivieren",
264
+ "settings.screenContext.disable": "Bildschirmkontext deaktivieren",
265
+ "settings.screenContext.enabled.description": "Die Freigabe des Bildschirmkontexts ist derzeit aktiviert",
266
+ "settings.screenContext.disabled.description": "Die Freigabe des Bildschirmkontexts ist derzeit deaktiviert",
267
+ "contextChip.label.paused": "Kontext: pausiert",
268
+ "contextChip.label.unavailable": "Kontext: nicht verf\xFCgbar",
269
+ "contextChip.label.softwareTemplates": "Software-Templates",
270
+ "contextChip.tooltip.askAbout": "Fragen Sie zu {{label}}.",
271
+ "contextChip.tooltip.template": "Fragen Sie, wie Sie das Template {{label}} ausf\xFCllen.",
272
+ "contextChip.tooltip.search": "Fragen Sie zu Ihrer Suche: {{label}}.",
273
+ "contextChip.tooltip.paused": "Der Bildschirmkontext ist pausiert. Klicken Sie, um die Freigabe Ihres aktuellen Bildschirms an den intelligenten Assistenten fortzusetzen.",
274
+ "contextChip.tooltip.unavailable": "Der Bildschirmkontext ist im Vollbildmodus nicht verf\xFCgbar. Wechseln Sie zum Overlay- oder Andockmodus, um ihn zu aktivieren.",
275
+ "contextChip.tooltip.line2.fullContext": "Seitentext und ein Screenshot werden mit Ihrer Nachricht gesendet.",
276
+ "contextChip.tooltip.line2.adminLimited": "Die Freigabe des Bildschirmkontexts ist durch Administratoreinstellungen eingeschr\xE4nkt.",
277
+ "contextChip.tooltip.line2.screenshotOnly": "Die Textextraktion ist von Ihrem Administrator deaktiviert. Nur Screenshot.",
278
+ "contextChip.tooltip.line2.domOffNoVision": "Die Textextraktion ist von Ihrem Administrator deaktiviert. Ihr Modell unterst\xFCtzt keine Bildanalyse.",
279
+ "contextChip.tooltip.line2.textOnlyNoVision": "Nur Textkontext \u2013 Ihr Modell unterst\xFCtzt keine Bildanalyse.",
280
+ "contextChip.tooltip.line2.textOnlyAdminScreenshotsOff": "Nur Textkontext \u2013 die Screenshot-Erfassung ist von Ihrem Administrator deaktiviert.",
281
+ "contextChip.tooltip.line2.textOnlyCombined": "Nur Textkontext \u2013 Ihr Modell unterst\xFCtzt keine Bildanalyse und die Screenshot-Erfassung ist von Ihrem Administrator deaktiviert.",
282
+ "contextChip.aria.pause": "Bildschirmkontext pausieren: {{label}}",
283
+ "contextChip.aria.resume": "Bildschirmkontext fortsetzen",
284
+ "savedPrompts.tab.title": "Gespeicherte Prompts",
285
+ "savedPrompts.disabled.title": "Gespeicherte Prompts sind deaktiviert",
286
+ "savedPrompts.disabled.body": "Gespeicherte Prompts sind im Chat-Verlaufsbereich ausgeblendet. Aktivieren Sie sie, um Ihre Prompts in der Seitenleiste anzuzeigen.",
287
+ "savedPrompts.disabled.enableLink": "Gespeicherte Prompts aktivieren",
288
+ "savedPrompts.count.zero": "Keine Prompts",
289
+ "savedPrompts.count_one": "1 Prompt",
290
+ "savedPrompts.count_other": "{{count}} Prompts",
291
+ "savedPrompts.newPrompt": "+ Neuer Prompt",
292
+ "savedPrompts.form.titleLabel": "Titel",
293
+ "savedPrompts.form.titlePlaceholder": "Prompt-Titel",
294
+ "savedPrompts.form.contentLabel": "Prompt",
295
+ "savedPrompts.form.contentPlaceholder": "Prompt-Inhalt",
296
+ "savedPrompts.form.save": "Speichern",
297
+ "savedPrompts.form.cancel": "Abbrechen",
298
+ "savedPrompts.validation.titleMaxLength": "Der Titel darf h\xF6chstens {{max}} Zeichen lang sein.",
299
+ "savedPrompts.validation.contentMaxLength": "Der Prompt darf h\xF6chstens {{max}} Zeichen lang sein.",
300
+ "savedPrompts.limitReached": "Prompt-Limit erreicht. L\xF6schen Sie einen vorhandenen Prompt, um einen neuen zu erstellen.",
301
+ "savedPrompts.actions.apply": "In Eingabefeld einf\xFCgen",
302
+ "savedPrompts.actions.send": "Direkt senden",
303
+ "savedPrompts.actions.sendDisabledStreaming": "Warten Sie, bis die Antwort abgeschlossen ist",
304
+ "savedPrompts.actions.delete": "L\xF6schen",
305
+ "savedPrompts.actions.menuAriaLabel": "Aktionen f\xFCr {{name}}",
306
+ "savedPrompts.delete.confirm.title": "\u201E{{name}}\u201C l\xF6schen?",
307
+ "savedPrompts.delete.confirm.message": "Dieser gespeicherte Prompt wird dauerhaft entfernt.",
308
+ "savedPrompts.delete.confirm.action": "L\xF6schen",
309
+ "savedPrompts.empty.description": "Speichern Sie h\xE4ufig verwendete Prompts, um sie in Ihren Unterhaltungen schnell wiederzuverwenden, ohne sie erneut einzugeben. Gespeicherte Prompts erscheinen auch im Chat-Verlaufsbereich f\xFCr den schnellen Zugriff.",
310
+ "savedPrompts.sidebar.showAll": "Alle anzeigen",
311
+ "savedPrompts.sidebar.showLess": "Weniger anzeigen",
312
+ "savedPrompts.sidebar.openSettings": "Einstellungen f\xFCr gespeicherte Prompts \xF6ffnen",
313
+ "savedPrompts.sidebar.empty": "Noch keine gespeicherten Prompts",
252
314
  "sort.alphabeticalAsc": "Name (A-Z)",
253
315
  "sort.alphabeticalDesc": "Name (Z-A)",
254
316
  "sort.label": "Unterhaltungen sortieren",
@@ -290,6 +352,7 @@ const intelligentAssistantTranslationDe = createTranslationMessages({
290
352
  "tooltip.quickNewChat": "Neuer Chat",
291
353
  "tooltip.responseRecorded": "Antwort aufgezeichnet",
292
354
  "tooltip.send": "Senden",
355
+ "tooltip.settings": "Chatbot-Optionen",
293
356
  "user.guest": "Gast",
294
357
  "user.loading": "..."
295
358
  }