@syntrologie/runtime-sdk 1.0.0-canary.1 → 1.0.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 (169) hide show
  1. package/CAPABILITIES.md +274 -31
  2. package/README.md +87 -0
  3. package/dist/RuntimeProvider.d.ts +1 -1
  4. package/dist/RuntimeProvider.js.map +1 -1
  5. package/dist/SmartCanvasApp.d.ts +4 -4
  6. package/dist/SmartCanvasApp.js +4 -4
  7. package/dist/SmartCanvasApp.js.map +1 -1
  8. package/dist/SmartCanvasElement.d.ts +2 -2
  9. package/dist/SmartCanvasElement.js +1 -1
  10. package/dist/SmartCanvasElement.js.map +1 -1
  11. package/dist/SmartCanvasPortal.js.map +1 -1
  12. package/dist/actions/ActionEngine.js +2 -4
  13. package/dist/actions/ActionEngine.js.map +1 -1
  14. package/dist/actions/executors/index.d.ts +1 -2
  15. package/dist/actions/executors/index.js +5 -7
  16. package/dist/actions/executors/index.js.map +1 -1
  17. package/dist/actions/executors/tour.js +3 -3
  18. package/dist/actions/executors/tour.js.map +1 -1
  19. package/dist/actions/validation.js +6 -31
  20. package/dist/actions/validation.js.map +1 -1
  21. package/dist/adaptives/adaptive-chatbot/index.js +9 -0
  22. package/dist/adaptives/adaptive-chatbot/index.js.map +7 -0
  23. package/dist/adaptives/adaptive-content/index.js +2 -0
  24. package/dist/adaptives/adaptive-content/index.js.map +7 -0
  25. package/dist/adaptives/adaptive-faq/index.js +11 -0
  26. package/dist/adaptives/adaptive-faq/index.js.map +7 -0
  27. package/dist/adaptives/adaptive-gamification/index.js +2 -0
  28. package/dist/adaptives/adaptive-gamification/index.js.map +7 -0
  29. package/dist/adaptives/adaptive-nav/index.js +11 -0
  30. package/dist/adaptives/adaptive-nav/index.js.map +7 -0
  31. package/dist/adaptives/adaptive-overlays/index.js +91 -0
  32. package/dist/adaptives/adaptive-overlays/index.js.map +7 -0
  33. package/dist/api.d.ts +5 -5
  34. package/dist/api.js +4 -7
  35. package/dist/api.js.map +1 -1
  36. package/dist/apps/AppContext.js +3 -5
  37. package/dist/apps/AppContext.js.map +1 -1
  38. package/dist/apps/AppLoader.d.ts +2 -1
  39. package/dist/apps/AppLoader.js +35 -9
  40. package/dist/apps/AppLoader.js.map +1 -1
  41. package/dist/apps/AppRegistry.js.map +1 -1
  42. package/dist/apps/examples/gamification-app.example.d.ts +8 -8
  43. package/dist/apps/examples/gamification-app.example.js.map +1 -1
  44. package/dist/apps/index.d.ts +0 -1
  45. package/dist/apps/index.js +0 -1
  46. package/dist/apps/index.js.map +1 -1
  47. package/dist/apps/types.d.ts +1 -1
  48. package/dist/blocks/data/ComparisonBlock.js +9 -9
  49. package/dist/blocks/data/ComparisonBlock.js.map +1 -1
  50. package/dist/blocks/data/StatsBlock.js +12 -14
  51. package/dist/blocks/data/StatsBlock.js.map +1 -1
  52. package/dist/blocks/index.d.ts +2 -2
  53. package/dist/blocks/index.js +3 -4
  54. package/dist/blocks/index.js.map +1 -1
  55. package/dist/blocks/interactive/ChecklistBlock.js +12 -12
  56. package/dist/blocks/interactive/ChecklistBlock.js.map +1 -1
  57. package/dist/blocks/interactive/RatingBlock.js +12 -14
  58. package/dist/blocks/interactive/RatingBlock.js.map +1 -1
  59. package/dist/blocks/notification/NotificationBlock.js +24 -24
  60. package/dist/blocks/notification/NotificationBlock.js.map +1 -1
  61. package/dist/bootstrap.d.ts +5 -5
  62. package/dist/bootstrap.js +30 -33
  63. package/dist/bootstrap.js.map +1 -1
  64. package/dist/components/ShadowCanvasOverlay.d.ts +2 -2
  65. package/dist/components/ShadowCanvasOverlay.js +25 -24
  66. package/dist/components/ShadowCanvasOverlay.js.map +1 -1
  67. package/dist/components/TileCard.d.ts +2 -2
  68. package/dist/components/TileCard.js +24 -23
  69. package/dist/components/TileCard.js.map +1 -1
  70. package/dist/components/TileWheel.d.ts +1 -1
  71. package/dist/components/TileWheel.js +23 -1
  72. package/dist/components/TileWheel.js.map +1 -1
  73. package/dist/configFetcher.d.ts +6 -2
  74. package/dist/configFetcher.js +65 -36
  75. package/dist/configFetcher.js.map +1 -1
  76. package/dist/context/ContextManager.d.ts +1 -1
  77. package/dist/context/ContextManager.js +4 -3
  78. package/dist/context/ContextManager.js.map +1 -1
  79. package/dist/context/schema.d.ts +8 -8
  80. package/dist/context/schema.js +1 -1
  81. package/dist/context/schema.js.map +1 -1
  82. package/dist/decisions/engine.d.ts +2 -2
  83. package/dist/decisions/engine.js.map +1 -1
  84. package/dist/decisions/schema.d.ts +34 -34
  85. package/dist/decisions/schema.js +1 -1
  86. package/dist/decisions/schema.js.map +1 -1
  87. package/dist/editorLoader.d.ts +19 -9
  88. package/dist/editorLoader.js +107 -96
  89. package/dist/editorLoader.js.map +1 -1
  90. package/dist/events/normalizers/canvas.d.ts +1 -1
  91. package/dist/events/normalizers/canvas.js.map +1 -1
  92. package/dist/events/schema.d.ts +4 -4
  93. package/dist/events/schema.js +1 -1
  94. package/dist/events/schema.js.map +1 -1
  95. package/dist/experiments/adapters/growthbook.d.ts +2 -1
  96. package/dist/experiments/adapters/growthbook.js +9 -1
  97. package/dist/experiments/adapters/growthbook.js.map +1 -1
  98. package/dist/experiments/types.d.ts +5 -0
  99. package/dist/fetchers/cdnFetcher.js.map +1 -1
  100. package/dist/fetchers/experimentsFetcher.d.ts +24 -2
  101. package/dist/fetchers/experimentsFetcher.js +48 -1
  102. package/dist/fetchers/experimentsFetcher.js.map +1 -1
  103. package/dist/fetchers/mergeConfigs.d.ts +29 -0
  104. package/dist/fetchers/mergeConfigs.js +38 -0
  105. package/dist/fetchers/mergeConfigs.js.map +1 -0
  106. package/dist/hooks/useCanvasOverlays.d.ts +1 -1
  107. package/dist/hooks/useCanvasOverlays.js +2 -2
  108. package/dist/hooks/useCanvasOverlays.js.map +1 -1
  109. package/dist/hooks/useHostPatches.js.map +1 -1
  110. package/dist/hooks/useShadowCanvasConfig.d.ts +1 -1
  111. package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
  112. package/dist/hostPatcher/core/patcher.js.map +1 -1
  113. package/dist/hostPatcher/policy/defaultPolicy.js.map +1 -1
  114. package/dist/index.js +15 -0
  115. package/dist/index.js.map +1 -1
  116. package/dist/overlays/fetcher.d.ts +1 -1
  117. package/dist/overlays/fetcher.js +12 -14
  118. package/dist/overlays/fetcher.js.map +1 -1
  119. package/dist/overlays/runtime/overlay/runner.js +18 -5
  120. package/dist/overlays/runtime/overlay/runner.js.map +1 -1
  121. package/dist/overlays/runtime/overlay/tooltip.js +3 -5
  122. package/dist/overlays/runtime/overlay/tooltip.js.map +1 -1
  123. package/dist/react.d.ts +1 -1
  124. package/dist/react.js.map +1 -1
  125. package/dist/render/RenderContext.js.map +1 -1
  126. package/dist/runtime.d.ts +6 -6
  127. package/dist/runtime.js +38 -16
  128. package/dist/runtime.js.map +1 -1
  129. package/dist/smart-canvas.esm.js +97 -85
  130. package/dist/smart-canvas.esm.js.map +4 -4
  131. package/dist/smart-canvas.js +41814 -44141
  132. package/dist/smart-canvas.js.map +4 -4
  133. package/dist/smart-canvas.min.js +97 -85
  134. package/dist/smart-canvas.min.js.map +4 -4
  135. package/dist/state/StateStore.d.ts +0 -6
  136. package/dist/state/StateStore.js +7 -1
  137. package/dist/state/StateStore.js.map +1 -1
  138. package/dist/surfaces/Surfaces.js +1 -1
  139. package/dist/surfaces/Surfaces.js.map +1 -1
  140. package/dist/surfaces/positioning.js.map +1 -1
  141. package/dist/telemetry/adapters/posthog.js +1 -1
  142. package/dist/telemetry/adapters/posthog.js.map +1 -1
  143. package/dist/telemetry/registry.d.ts +0 -7
  144. package/dist/telemetry/registry.js +1 -1
  145. package/dist/telemetry/registry.js.map +1 -1
  146. package/dist/theme/ThemeProvider.js.map +1 -1
  147. package/dist/theme/defaultTheme.d.ts +2 -3
  148. package/dist/theme/defaultTheme.js +60 -51
  149. package/dist/theme/defaultTheme.js.map +1 -1
  150. package/dist/theme/extractHostTheme.js +1 -1
  151. package/dist/theme/extractHostTheme.js.map +1 -1
  152. package/dist/types.d.ts +1 -1
  153. package/dist/types.js +0 -1
  154. package/dist/types.js.map +1 -1
  155. package/dist/version.d.ts +1 -1
  156. package/dist/version.js +1 -1
  157. package/dist/version.js.map +1 -1
  158. package/dist/widgets/WidgetRegistry.d.ts +6 -0
  159. package/dist/widgets/WidgetRegistry.js +12 -3
  160. package/dist/widgets/WidgetRegistry.js.map +1 -1
  161. package/package.json +20 -16
  162. package/dist/apps/adaptive-chatbot/index.js +0 -7
  163. package/dist/apps/adaptive-chatbot/index.js.map +0 -7
  164. package/dist/apps/faq/index.js +0 -11
  165. package/dist/apps/faq/index.js.map +0 -7
  166. package/dist/apps/gamification/index.js +0 -2
  167. package/dist/apps/gamification/index.js.map +0 -7
  168. package/dist/apps/nav/index.js +0 -11
  169. package/dist/apps/nav/index.js.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntrologie/runtime-sdk",
3
- "version": "1.0.0-canary.1",
3
+ "version": "1.0.0",
4
4
  "description": "Syntrologie Runtime SDK for web experimentation and analytics",
5
5
  "license": "Proprietary",
6
6
  "private": false,
@@ -54,12 +54,12 @@
54
54
  "prepublishOnly": "npm run clean && npm run build"
55
55
  },
56
56
  "dependencies": {
57
- "@floating-ui/dom": "~1.6.13",
57
+ "@floating-ui/dom": "^1.7.5",
58
58
  "@growthbook/growthbook": "~1.6.2",
59
59
  "@growthbook/growthbook-react": "~1.6.2",
60
- "@syntrologie/adapt-content": "1.0.0-canary.1",
61
- "@syntrologie/adapt-navigation": "1.0.0-canary.1",
62
- "@syntrologie/adapt-overlays": "1.0.0-canary.1",
60
+ "@syntro/design-system": "*",
61
+ "@syntrologie/adapt-content": "1.0.0",
62
+ "@syntrologie/adapt-overlays": "1.0.0",
63
63
  "posthog-js": "~1.302.2",
64
64
  "zod": "~3.25.0"
65
65
  },
@@ -68,17 +68,21 @@
68
68
  "react-dom": ">=18.0.0"
69
69
  },
70
70
  "devDependencies": {
71
- "@types/node": "^25.0.9",
72
- "@types/react": "~18.3.0",
73
- "@types/react-dom": "~18.3.0",
74
- "esbuild": "~0.25.0",
75
- "react": "^18.3.1",
76
- "react-dom": "^18.3.1",
77
- "typescript": "~5.7.0",
78
- "vitest": "^4.0.18",
79
- "semantic-release": "~25.0.3",
80
- "@semantic-release/npm": "~13.1.3",
81
71
  "@semantic-release/exec": "~7.1.0",
82
- "@semantic-release/github": "~12.0.3"
72
+ "@semantic-release/github": "~12.0.3",
73
+ "@semantic-release/npm": "~13.1.3",
74
+ "@testing-library/dom": "^10.4.1",
75
+ "@testing-library/jest-dom": "^6.9.1",
76
+ "@testing-library/react": "^16.3.2",
77
+ "@types/node": "^25.2.0",
78
+ "@types/react": "^19.1.0",
79
+ "@types/react-dom": "^19.1.0",
80
+ "esbuild": "^0.27.2",
81
+ "jsdom": "^25.0.1",
82
+ "react": "^19.2.4",
83
+ "react-dom": "^19.2.4",
84
+ "semantic-release": "~25.0.3",
85
+ "typescript": "~5.7.0",
86
+ "vitest": "^4.0.18"
83
87
  }
84
88
  }
@@ -1,7 +0,0 @@
1
- import{useRef as R,useEffect as M}from"react";import{useState as g,useCallback as f,useRef as m}from"react";import{jsx as A,jsxs as I}from"react/jsx-runtime";var n={mount(t,r){let{config:s,runtime:i,tileId:a="chatbot-widget"}=r||{};return!s||!i?(t.innerHTML='<div style="padding: 16px; color: #94a3b8;">Chat widget requires config and runtime.</div>',()=>{t.innerHTML=""}):(t.innerHTML=`
2
- <div style="padding: 16px; font-family: system-ui; color: #e2e8f0;">
3
- <p style="margin: 0 0 8px; color: #94a3b8;">${s.greeting||"Hi! How can I help?"}</p>
4
- <p style="margin: 0; font-size: 12px; color: #64748b;">Chat widget mounted. Awaiting React renderer.</p>
5
- </div>
6
- `,()=>{t.innerHTML=""})}};var e={id:"adaptive-chatbot",version:"1.0.0",name:"Chat Assistant",description:"AI chat assistant with action execution capabilities",executors:[],widgets:[{id:"adaptive-chatbot:assistant",component:n,metadata:{name:"Chat Assistant",description:"AI-powered chat assistant that can execute DOM actions",icon:"\u{1F4AC}"}}]};var o={id:"adaptive-chatbot",version:e.version,name:e.name,description:e.description,runtime:{actions:[],widgets:e.widgets},metadata:{isBuiltIn:!1}};if(typeof window<"u"){let t=window.__SYNOS_APP_REGISTRY__;t&&typeof t.register=="function"&&t.register(o)}var T=o;export{T as default,o as manifest};
7
- //# sourceMappingURL=index.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../adaptives/adaptive-chatbot/src/ChatAssistant.tsx", "../../../../adaptives/adaptive-chatbot/src/useChat.ts", "../../../../adaptives/adaptive-chatbot/src/runtime.ts", "../../../../adaptives/adaptive-chatbot/src/cdn.ts"],
4
- "sourcesContent": ["/**\n * Adaptive Chatbot - ChatAssistant Component\n *\n * Main React component for the AI chat assistant widget.\n * Renders a message list with auto-scroll, loading indicator, and input form.\n */\n\nimport React, { useRef, useEffect } from 'react';\nimport { useChat } from './useChat';\nimport type { ChatMessage, ChatbotWidgetRuntime, ChatbotConfig } from './types';\n\n// ============================================================================\n// Styles\n// ============================================================================\n\nconst styles = {\n container: {\n display: 'flex',\n flexDirection: 'column' as const,\n height: '100%',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n fontSize: '14px',\n },\n messageList: {\n flex: 1,\n overflowY: 'auto' as const,\n padding: '12px',\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '8px',\n },\n messageBubble: {\n maxWidth: '85%',\n padding: '8px 12px',\n borderRadius: '12px',\n lineHeight: 1.5,\n wordBreak: 'break-word' as const,\n },\n userMessage: {\n alignSelf: 'flex-end' as const,\n backgroundColor: '#6366f1',\n color: '#ffffff',\n borderBottomRightRadius: '4px',\n },\n assistantMessage: {\n alignSelf: 'flex-start' as const,\n backgroundColor: 'rgba(255, 255, 255, 0.08)',\n color: '#e2e8f0',\n borderBottomLeftRadius: '4px',\n },\n loadingDots: {\n alignSelf: 'flex-start' as const,\n padding: '8px 16px',\n backgroundColor: 'rgba(255, 255, 255, 0.05)',\n borderRadius: '12px',\n color: '#94a3b8',\n fontSize: '13px',\n },\n errorBanner: {\n padding: '8px 12px',\n backgroundColor: 'rgba(239, 68, 68, 0.1)',\n color: '#ef4444',\n fontSize: '13px',\n borderRadius: '8px',\n margin: '0 12px',\n },\n inputForm: {\n display: 'flex',\n gap: '8px',\n padding: '12px',\n borderTop: '1px solid rgba(255, 255, 255, 0.06)',\n },\n input: {\n flex: 1,\n padding: '8px 12px',\n borderRadius: '8px',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n backgroundColor: 'rgba(0, 0, 0, 0.2)',\n color: '#f1f5f9',\n fontSize: '14px',\n outline: 'none',\n fontFamily: 'inherit',\n },\n sendButton: {\n padding: '8px 16px',\n borderRadius: '8px',\n border: 'none',\n backgroundColor: '#6366f1',\n color: '#ffffff',\n fontWeight: 600,\n fontSize: '13px',\n cursor: 'pointer',\n whiteSpace: 'nowrap' as const,\n },\n sendButtonDisabled: {\n opacity: 0.5,\n cursor: 'not-allowed' as const,\n },\n};\n\n// ============================================================================\n// MessageBubble Component\n// ============================================================================\n\nfunction MessageBubble({ message }: { message: ChatMessage }) {\n const isUser = message.role === 'user';\n const bubbleStyle = {\n ...styles.messageBubble,\n ...(isUser ? styles.userMessage : styles.assistantMessage),\n };\n\n return <div style={bubbleStyle}>{message.text}</div>;\n}\n\n// ============================================================================\n// ChatAssistant Component\n// ============================================================================\n\nexport interface ChatAssistantProps {\n config: ChatbotConfig;\n runtime: ChatbotWidgetRuntime;\n tileId: string;\n}\n\nexport function ChatAssistant({ config, runtime, tileId }: ChatAssistantProps) {\n const {\n messages,\n isLoading,\n error,\n sendMessage,\n clearMessages: _clearMessages,\n } = useChat({\n backendUrl: config.backendUrl,\n tileId,\n runtime,\n greeting: config.greeting,\n maxHistory: config.maxHistory,\n mlflowRunId: config.mlflowRunId,\n });\n\n const messageListRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n // Auto-scroll to bottom on new messages\n useEffect(() => {\n if (messageListRef.current) {\n messageListRef.current.scrollTop = messageListRef.current.scrollHeight;\n }\n }, [messages, isLoading]);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const input = inputRef.current;\n if (!input || !input.value.trim() || isLoading) return;\n\n const text = input.value;\n input.value = '';\n sendMessage(text);\n };\n\n return (\n <div style={styles.container} data-testid=\"chat-assistant\">\n {/* Message list */}\n <div ref={messageListRef} style={styles.messageList}>\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n {isLoading && <div style={styles.loadingDots}>Thinking...</div>}\n </div>\n\n {/* Error banner */}\n {error && <div style={styles.errorBanner}>{error}</div>}\n\n {/* Input form */}\n <form onSubmit={handleSubmit} style={styles.inputForm}>\n <input\n ref={inputRef}\n style={styles.input}\n placeholder=\"Ask anything...\"\n disabled={isLoading}\n data-testid=\"chat-input\"\n />\n <button\n type=\"submit\"\n disabled={isLoading}\n style={{\n ...styles.sendButton,\n ...(isLoading ? styles.sendButtonDisabled : {}),\n }}\n data-testid=\"chat-send\"\n >\n Send\n </button>\n </form>\n </div>\n );\n}\n\n// ============================================================================\n// Mountable Widget Interface\n// ============================================================================\n\nexport const ChatAssistantMountableWidget = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>) {\n const {\n config,\n runtime,\n tileId: _tileId = 'chatbot-widget',\n } = (mountConfig || {}) as {\n config?: ChatbotConfig;\n runtime?: ChatbotWidgetRuntime;\n tileId?: string;\n };\n\n if (!config || !runtime) {\n container.innerHTML =\n '<div style=\"padding: 16px; color: #94a3b8;\">Chat widget requires config and runtime.</div>';\n return () => {\n container.innerHTML = '';\n };\n }\n\n // Simple HTML fallback \u2014 full React rendering is handled by the runtime\n container.innerHTML = `\n <div style=\"padding: 16px; font-family: system-ui; color: #e2e8f0;\">\n <p style=\"margin: 0 0 8px; color: #94a3b8;\">${config.greeting || 'Hi! How can I help?'}</p>\n <p style=\"margin: 0; font-size: 12px; color: #64748b;\">Chat widget mounted. Awaiting React renderer.</p>\n </div>\n `;\n\n return () => {\n container.innerHTML = '';\n };\n },\n};\n\nexport default ChatAssistant;\n", "/**\n * Adaptive Chatbot - useChat Hook\n *\n * React hook managing chat message state, API communication,\n * history management, and action execution via the runtime ActionEngine.\n */\n\nimport { useState, useCallback, useRef } from 'react';\nimport { sendMessage } from './apiClient';\nimport { parseActions } from './actionParser';\nimport type { ChatMessage, ChatbotWidgetRuntime, BatchActionHandle } from './types';\n\nexport interface UseChatOptions {\n backendUrl: string;\n tileId: string;\n runtime: ChatbotWidgetRuntime;\n greeting?: string;\n maxHistory?: number;\n mlflowRunId?: string;\n config?: Record<string, unknown>;\n}\n\nexport interface UseChatReturn {\n messages: ChatMessage[];\n isLoading: boolean;\n error: string | null;\n sendMessage: (text: string) => Promise<void>;\n clearMessages: () => void;\n}\n\nlet nextId = 0;\nfunction generateId(): string {\n return `msg-${Date.now()}-${++nextId}`;\n}\n\nexport function useChat(options: UseChatOptions): UseChatReturn {\n const { backendUrl, tileId, runtime, greeting, maxHistory = 20, mlflowRunId, config } = options;\n\n const [messages, setMessages] = useState<ChatMessage[]>(() => {\n if (greeting) {\n return [\n {\n id: generateId(),\n role: 'assistant' as const,\n text: greeting,\n timestamp: Date.now(),\n },\n ];\n }\n return [];\n });\n\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const batchHandleRef = useRef<BatchActionHandle | null>(null);\n\n const send = useCallback(\n async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n\n setError(null);\n\n const userMessage: ChatMessage = {\n id: generateId(),\n role: 'user',\n text: trimmed,\n timestamp: Date.now(),\n };\n\n setMessages((prev) => [...prev, userMessage]);\n setIsLoading(true);\n\n try {\n // Build history from current messages + new user message\n const currentMessages = [...messages, userMessage];\n const historySlice = currentMessages.slice(-maxHistory).map((m) => ({\n role: m.role,\n content: m.text,\n }));\n\n const response = await sendMessage(backendUrl, {\n message: trimmed,\n history: historySlice,\n mlflow_run_id: mlflowRunId,\n current_config: config,\n });\n\n // Parse actions from the LLM response\n const { displayText, actions } = parseActions(response.response);\n\n const assistantMessage: ChatMessage = {\n id: generateId(),\n role: 'assistant',\n text: displayText,\n timestamp: Date.now(),\n };\n\n setMessages((prev) => [...prev, assistantMessage]);\n\n // Execute actions if present\n if (actions.length > 0) {\n // Revert previous batch\n if (batchHandleRef.current?.isApplied()) {\n await batchHandleRef.current.revertAll();\n }\n\n batchHandleRef.current = await runtime.actions.applyBatch(actions);\n\n runtime.events.publish('chatbot.actions_applied', {\n count: actions.length,\n kinds: actions.map((a) => a.kind),\n tileId,\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An unexpected error occurred';\n setError(message);\n } finally {\n setIsLoading(false);\n }\n },\n [backendUrl, messages, maxHistory, mlflowRunId, config, runtime, tileId]\n );\n\n const clearMessages = useCallback(() => {\n setMessages([]);\n setError(null);\n // Revert any active actions\n if (batchHandleRef.current?.isApplied()) {\n batchHandleRef.current.revertAll();\n batchHandleRef.current = null;\n }\n sessionStorage.removeItem(`syntro:chatbot:history:${tileId}`);\n }, [tileId]);\n\n return {\n messages,\n isLoading,\n error,\n sendMessage: send,\n clearMessages,\n };\n}\n", "/**\n * Adaptive Chatbot - Runtime Module\n *\n * Runtime manifest for the AI chat assistant adaptive.\n * This is a widget-based adaptive with no action executors.\n */\n\nimport { ChatAssistantMountableWidget } from './ChatAssistant';\n\n// ============================================================================\n// App Runtime Manifest\n// ============================================================================\n\nexport const runtime = {\n id: 'adaptive-chatbot',\n version: '1.0.0',\n name: 'Chat Assistant',\n description: 'AI chat assistant with action execution capabilities',\n\n /** No action executors \u2014 chatbot uses existing action kinds via applyBatch */\n executors: [],\n\n /** Widget definitions for the runtime's WidgetRegistry */\n widgets: [\n {\n id: 'adaptive-chatbot:assistant',\n component: ChatAssistantMountableWidget,\n metadata: {\n name: 'Chat Assistant',\n description: 'AI-powered chat assistant that can execute DOM actions',\n icon: '\uD83D\uDCAC',\n },\n },\n ],\n};\n\nexport default runtime;\n", "/**\n * CDN Entry Point for Adaptive Chatbot\n *\n * This module is bundled for CDN delivery and self-registers with the global\n * SynOS app registry when loaded dynamically via the AppLoader.\n */\n\nimport { runtime } from './runtime';\n\n/**\n * App manifest for registry registration.\n * Follows the AppManifest interface expected by AppLoader/AppRegistry.\n */\nexport const manifest = {\n id: 'adaptive-chatbot',\n version: runtime.version,\n name: runtime.name,\n description: runtime.description,\n runtime: {\n actions: [],\n widgets: runtime.widgets,\n },\n metadata: {\n isBuiltIn: false,\n },\n};\n\n/**\n * Self-register with global registry if available.\n * This happens when loaded via script tag (UMD).\n */\nif (typeof window !== 'undefined') {\n const globalRegistry = (window as any).__SYNOS_APP_REGISTRY__;\n if (globalRegistry && typeof globalRegistry.register === 'function') {\n globalRegistry.register(manifest);\n }\n}\n\nexport default manifest;\n"],
5
- "mappings": "AAOA,OAAgB,UAAAA,EAAQ,aAAAC,MAAiB,QCAzC,OAAS,YAAAC,EAAU,eAAAC,EAAa,UAAAC,MAAc,QDwGrC,cAAAC,EAoDH,QAAAC,MApDG,oBA2FF,IAAMC,EAA+B,CAC1C,MAAMC,EAAwBC,EAAuC,CACnE,GAAM,CACJ,OAAAC,EACA,QAAAC,EACA,OAAQC,EAAU,gBACpB,EAAKH,GAAe,CAAC,EAMrB,MAAI,CAACC,GAAU,CAACC,GACdH,EAAU,UACR,6FACK,IAAM,CACXA,EAAU,UAAY,EACxB,IAIFA,EAAU,UAAY;AAAA;AAAA,sDAE4BE,EAAO,UAAY,qBAAqB;AAAA;AAAA;AAAA,MAKnF,IAAM,CACXF,EAAU,UAAY,EACxB,EACF,CACF,EE7NO,IAAMK,EAAU,CACrB,GAAI,mBACJ,QAAS,QACT,KAAM,iBACN,YAAa,uDAGb,UAAW,CAAC,EAGZ,QAAS,CACP,CACE,GAAI,6BACJ,UAAWC,EACX,SAAU,CACR,KAAM,iBACN,YAAa,yDACb,KAAM,WACR,CACF,CACF,CACF,ECrBO,IAAMC,EAAW,CACtB,GAAI,mBACJ,QAASC,EAAQ,QACjB,KAAMA,EAAQ,KACd,YAAaA,EAAQ,YACrB,QAAS,CACP,QAAS,CAAC,EACV,QAASA,EAAQ,OACnB,EACA,SAAU,CACR,UAAW,EACb,CACF,EAMA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAMC,EAAkB,OAAe,uBACnCA,GAAkB,OAAOA,EAAe,UAAa,YACvDA,EAAe,SAASF,CAAQ,CAEpC,CAEA,IAAOG,EAAQH",
6
- "names": ["useRef", "useEffect", "useState", "useCallback", "useRef", "jsx", "jsxs", "ChatAssistantMountableWidget", "container", "mountConfig", "config", "runtime", "_tileId", "runtime", "ChatAssistantMountableWidget", "manifest", "runtime", "globalRegistry", "cdn_default"]
7
- }
@@ -1,11 +0,0 @@
1
- import{useEffect as u,useReducer as f,useMemo as g,useCallback as m,useState as x}from"react";import{jsx as b,jsxs as y}from"react/jsx-runtime";var n={mount(e,r){let{runtime:s,instanceId:d="faq-widget",...i}=r||{expandBehavior:"single",searchable:!1,theme:"auto",actions:[]};if(!s){let c=i.actions||[];e.innerHTML=`
2
- <div style="font-family: system-ui; max-width: 800px;">
3
- ${c.map(o=>`
4
- <div style="margin-bottom: 8px; padding: 16px; background: #f9fafb; border-radius: 8px;">
5
- <strong>${o.config.question}</strong>
6
- <p style="margin-top: 8px; color: #4b5563;">${o.config.answer}</p>
7
- </div>
8
- `).join("")}
9
- </div>
10
- `}return()=>{e.innerHTML=""}}};var t={id:"adaptive-faq",version:"1.0.0",name:"FAQ Accordion",description:"Collapsible Q&A accordion with per-item conditional visibility",executors:[],widgets:[{id:"adaptive-faq:accordion",component:n,metadata:{name:"FAQ Accordion",description:"Collapsible Q&A accordion with search",icon:"\u2753"}}]};var a={id:"faq",version:t.version,name:t.name,description:t.description,runtime:{actions:[],widgets:t.widgets},metadata:{isBuiltIn:!1}};if(typeof window<"u"){let e=window.__SYNOS_APP_REGISTRY__;e&&typeof e.register=="function"&&e.register(a)}var A=a;export{A as default,a as manifest};
11
- //# sourceMappingURL=index.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../adaptives/adaptive-faq/src/FAQWidget.tsx", "../../../../adaptives/adaptive-faq/src/runtime.ts", "../../../../adaptives/adaptive-faq/src/cdn.ts"],
4
- "sourcesContent": ["/**\n * Adaptive FAQ - FAQWidget Component\n *\n * React component that renders a collapsible Q&A accordion with per-item\n * conditional visibility based on showWhen decision strategies.\n *\n * Demonstrates the compositional action pattern where child actions\n * (faq:question) serve as configuration data for the parent widget.\n */\n\nimport React, { useEffect, useReducer, useMemo, useCallback, useState } from 'react';\nimport type { FAQWidgetProps, FAQQuestionAction, FAQConfig, FAQWidgetRuntime } from './types';\n\n// ============================================================================\n// Styles\n// ============================================================================\n\nconst baseStyles = {\n container: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n maxWidth: '800px',\n margin: '0 auto',\n },\n searchWrapper: {\n marginBottom: '16px',\n },\n searchInput: {\n width: '100%',\n padding: '12px 16px',\n borderRadius: '8px',\n fontSize: '14px',\n outline: 'none',\n transition: 'border-color 0.15s ease',\n },\n accordion: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '8px',\n },\n item: {\n borderRadius: '8px',\n overflow: 'hidden',\n transition: 'box-shadow 0.15s ease',\n },\n question: {\n width: '100%',\n padding: '16px 20px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n border: 'none',\n cursor: 'pointer',\n fontSize: '15px',\n fontWeight: 500,\n textAlign: 'left' as const,\n transition: 'background-color 0.15s ease',\n },\n chevron: {\n fontSize: '18px',\n transition: 'transform 0.2s ease',\n },\n answer: {\n padding: '0 20px 16px 20px',\n fontSize: '14px',\n lineHeight: 1.6,\n overflow: 'hidden',\n transition: 'max-height 0.2s ease, padding 0.2s ease',\n },\n category: {\n display: 'inline-block',\n fontSize: '11px',\n fontWeight: 600,\n textTransform: 'uppercase' as const,\n letterSpacing: '0.05em',\n padding: '4px 8px',\n borderRadius: '4px',\n marginBottom: '8px',\n },\n emptyState: {\n textAlign: 'center' as const,\n padding: '48px 24px',\n fontSize: '14px',\n },\n noResults: {\n textAlign: 'center' as const,\n padding: '32px 16px',\n fontSize: '14px',\n },\n} as const;\n\nconst themeStyles = {\n light: {\n container: {\n backgroundColor: '#ffffff',\n color: '#111827',\n },\n searchInput: {\n backgroundColor: '#f9fafb',\n border: '1px solid #e5e7eb',\n color: '#111827',\n },\n item: {\n backgroundColor: '#f9fafb',\n border: '1px solid #e5e7eb',\n },\n itemExpanded: {\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',\n },\n question: {\n backgroundColor: 'transparent',\n color: '#111827',\n },\n questionHover: {\n backgroundColor: '#f3f4f6',\n },\n answer: {\n color: '#4b5563',\n },\n category: {\n backgroundColor: '#e0e7ff',\n color: '#4338ca',\n },\n emptyState: {\n color: '#9ca3af',\n },\n },\n dark: {\n container: {\n backgroundColor: '#111827',\n color: '#f9fafb',\n },\n searchInput: {\n backgroundColor: '#1f2937',\n border: '1px solid #374151',\n color: '#f9fafb',\n },\n item: {\n backgroundColor: '#1f2937',\n border: '1px solid #374151',\n },\n itemExpanded: {\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',\n },\n question: {\n backgroundColor: 'transparent',\n color: '#f9fafb',\n },\n questionHover: {\n backgroundColor: '#374151',\n },\n answer: {\n color: '#9ca3af',\n },\n category: {\n backgroundColor: '#312e81',\n color: '#a5b4fc',\n },\n emptyState: {\n color: '#6b7280',\n },\n },\n} as const;\n\n// ============================================================================\n// FAQItem Component\n// ============================================================================\n\ninterface FAQItemProps {\n item: FAQQuestionAction;\n isExpanded: boolean;\n onToggle: () => void;\n theme: 'light' | 'dark';\n}\n\nfunction FAQItem({ item, isExpanded, onToggle, theme }: FAQItemProps) {\n const [isHovered, setIsHovered] = useState(false);\n const colors = themeStyles[theme];\n const { question, answer, category } = item.config;\n\n const itemStyle: React.CSSProperties = {\n ...baseStyles.item,\n ...colors.item,\n ...(isExpanded ? colors.itemExpanded : {}),\n };\n\n const questionStyle: React.CSSProperties = {\n ...baseStyles.question,\n ...colors.question,\n ...(isHovered ? colors.questionHover : {}),\n };\n\n const chevronStyle: React.CSSProperties = {\n ...baseStyles.chevron,\n transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',\n };\n\n const answerStyle: React.CSSProperties = {\n ...baseStyles.answer,\n ...colors.answer,\n maxHeight: isExpanded ? '500px' : '0',\n paddingBottom: isExpanded ? '16px' : '0',\n };\n\n const categoryStyle: React.CSSProperties = {\n ...baseStyles.category,\n ...colors.category,\n };\n\n return (\n <div style={itemStyle}>\n <button\n style={questionStyle}\n onClick={onToggle}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n aria-expanded={isExpanded}\n >\n <span>{question}</span>\n <span style={chevronStyle}>\u25BC</span>\n </button>\n <div style={answerStyle} aria-hidden={!isExpanded}>\n {category && <span style={categoryStyle}>{category}</span>}\n <p style={{ margin: 0 }}>{answer}</p>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// FAQWidget Component\n// ============================================================================\n\n/**\n * FAQWidget - Renders a collapsible Q&A accordion with per-item activation.\n *\n * This component demonstrates the compositional action pattern:\n * - Parent (FAQWidget) receives `config.actions` array\n * - Each action has optional `showWhen` for per-item visibility\n * - Parent evaluates showWhen and filters visible questions\n * - Parent manages expand state and re-rendering on context changes\n */\nexport function FAQWidget({ config, runtime, instanceId }: FAQWidgetProps) {\n // Force re-render when context changes\n const [, forceUpdate] = useReducer((x) => x + 1, 0);\n\n // Track expanded question IDs\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());\n\n // Search query state\n const [searchQuery, setSearchQuery] = useState('');\n\n // Subscribe to context changes for reactive updates\n useEffect(() => {\n const unsubscribe = runtime.context.subscribe(() => {\n forceUpdate();\n });\n return unsubscribe;\n }, [runtime.context]);\n\n // Filter visible questions based on per-item showWhen\n const visibleQuestions = useMemo(() => {\n return config.actions.filter((q) => {\n // No showWhen = always visible\n if (!q.showWhen) return true;\n\n // Evaluate the decision strategy\n const result = runtime.evaluateSync<boolean>(q.showWhen);\n return result.value;\n });\n }, [config.actions, runtime]);\n\n // Apply search filter\n const filteredQuestions = useMemo(() => {\n if (!config.searchable || !searchQuery.trim()) {\n return visibleQuestions;\n }\n\n const query = searchQuery.toLowerCase();\n return visibleQuestions.filter(\n (q) =>\n q.config.question.toLowerCase().includes(query) ||\n q.config.answer.toLowerCase().includes(query) ||\n q.config.category?.toLowerCase().includes(query)\n );\n }, [visibleQuestions, searchQuery, config.searchable]);\n\n // Resolve theme (auto \u2192 detect system preference)\n const resolvedTheme = useMemo(() => {\n if (config.theme !== 'auto') return config.theme;\n\n // Check system preference (SSR-safe)\n if (typeof window !== 'undefined') {\n return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n return 'light';\n }, [config.theme]);\n\n // Handle question toggle\n const handleToggle = useCallback(\n (id: string) => {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n\n if (config.expandBehavior === 'single') {\n // Single mode: collapse all others\n if (prev.has(id)) {\n return new Set();\n }\n return new Set([id]);\n } else {\n // Multiple mode: toggle this one\n if (prev.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n }\n });\n\n // Publish toggle event for analytics\n runtime.events.publish('faq:toggled', {\n instanceId,\n questionId: id,\n expanded: !expandedIds.has(id),\n timestamp: Date.now(),\n });\n },\n [config.expandBehavior, runtime.events, instanceId, expandedIds]\n );\n\n // Compute styles\n const containerStyle: React.CSSProperties = {\n ...baseStyles.container,\n ...themeStyles[resolvedTheme].container,\n };\n\n const searchInputStyle: React.CSSProperties = {\n ...baseStyles.searchInput,\n ...themeStyles[resolvedTheme].searchInput,\n };\n\n const emptyStateStyle: React.CSSProperties = {\n ...baseStyles.emptyState,\n ...themeStyles[resolvedTheme].emptyState,\n };\n\n // Empty state (no visible questions at all)\n if (visibleQuestions.length === 0) {\n return (\n <div style={containerStyle} data-adaptive-id={instanceId} data-adaptive-type=\"adaptive-faq\">\n <div style={emptyStateStyle}>No FAQ questions available.</div>\n </div>\n );\n }\n\n return (\n <div style={containerStyle} data-adaptive-id={instanceId} data-adaptive-type=\"adaptive-faq\">\n {/* Search input */}\n {config.searchable && (\n <div style={baseStyles.searchWrapper}>\n <input\n type=\"text\"\n placeholder=\"Search questions...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={searchInputStyle}\n />\n </div>\n )}\n\n {/* Accordion */}\n <div style={baseStyles.accordion}>\n {filteredQuestions.map((q) => (\n <FAQItem\n key={q.config.id}\n item={q}\n isExpanded={expandedIds.has(q.config.id)}\n onToggle={() => handleToggle(q.config.id)}\n theme={resolvedTheme}\n />\n ))}\n </div>\n\n {/* No search results */}\n {config.searchable && filteredQuestions.length === 0 && searchQuery && (\n <div style={{ ...baseStyles.noResults, ...themeStyles[resolvedTheme].emptyState }}>\n No questions found matching \"{searchQuery}\"\n </div>\n )}\n </div>\n );\n}\n\n// ============================================================================\n// Mountable Widget Interface\n// ============================================================================\n\n/**\n * Mountable widget interface for the runtime's WidgetRegistry.\n */\nexport const FAQMountableWidget = {\n mount(\n container: HTMLElement,\n config?: FAQConfig & { runtime?: FAQWidgetRuntime; instanceId?: string }\n ) {\n // This is a simplified mount for non-React environments\n // In practice, the runtime handles React rendering\n\n const {\n runtime,\n instanceId: _instanceId = 'faq-widget',\n ...faqConfig\n } = config || {\n expandBehavior: 'single' as const,\n searchable: false,\n theme: 'auto' as const,\n actions: [],\n };\n\n // Create simple HTML fallback if no runtime\n if (!runtime) {\n const questions = faqConfig.actions || [];\n container.innerHTML = `\n <div style=\"font-family: system-ui; max-width: 800px;\">\n ${questions\n .map(\n (q) => `\n <div style=\"margin-bottom: 8px; padding: 16px; background: #f9fafb; border-radius: 8px;\">\n <strong>${q.config.question}</strong>\n <p style=\"margin-top: 8px; color: #4b5563;\">${q.config.answer}</p>\n </div>\n `\n )\n .join('')}\n </div>\n `;\n }\n\n return () => {\n container.innerHTML = '';\n };\n },\n};\n\nexport default FAQWidget;\n", "/**\n * Adaptive FAQ - Runtime Module\n *\n * Runtime manifest for the FAQ accordion adaptive.\n * This is a widget-based adaptive with no action executors.\n */\n\nimport { FAQMountableWidget } from './FAQWidget';\n\n// ============================================================================\n// App Runtime Manifest\n// ============================================================================\n\n/**\n * Runtime manifest for adaptive-faq.\n *\n * Note: This adaptive is widget-based, not action-based.\n * The `faq:question` actions are compositional - they're rendered by\n * the widget, not executed by the runtime.\n */\nexport const runtime = {\n id: 'adaptive-faq',\n version: '1.0.0',\n name: 'FAQ Accordion',\n description: 'Collapsible Q&A accordion with per-item conditional visibility',\n\n /**\n * No action executors - faq:question actions are compositional,\n * meaning they serve as configuration for the FAQWidget.\n */\n executors: [],\n\n /**\n * Widget definitions for the runtime's WidgetRegistry.\n */\n widgets: [\n {\n id: 'adaptive-faq:accordion',\n component: FAQMountableWidget,\n metadata: {\n name: 'FAQ Accordion',\n description: 'Collapsible Q&A accordion with search',\n icon: '\u2753',\n },\n },\n ],\n};\n\nexport default runtime;\n", "/**\n * CDN Entry Point for Adaptive FAQ\n *\n * This module is bundled for CDN delivery and self-registers with the global\n * SynOS app registry when loaded dynamically via the AppLoader.\n */\n\nimport { runtime } from './runtime';\n\n/**\n * App manifest for registry registration.\n * Follows the AppManifest interface expected by AppLoader/AppRegistry.\n */\nexport const manifest = {\n id: 'faq',\n version: runtime.version,\n name: runtime.name,\n description: runtime.description,\n runtime: {\n // FAQ is widget-based, no action executors\n actions: [],\n widgets: runtime.widgets,\n },\n metadata: {\n isBuiltIn: false,\n },\n};\n\n/**\n * Self-register with global registry if available.\n * This happens when loaded via script tag (UMD).\n */\nif (typeof window !== 'undefined') {\n const globalRegistry = (window as any).__SYNOS_APP_REGISTRY__;\n if (globalRegistry && typeof globalRegistry.register === 'function') {\n globalRegistry.register(manifest);\n }\n}\n\nexport default manifest;\n"],
5
- "mappings": "AAUA,OAAgB,aAAAA,EAAW,cAAAC,EAAY,WAAAC,EAAS,eAAAC,EAAa,YAAAC,MAAgB,QAwMvE,OAOE,OAAAC,EAPF,QAAAC,MAAA,oBA+LC,IAAMC,EAAqB,CAChC,MACEC,EACAC,EACA,CAIA,GAAM,CACJ,QAAAC,EACA,WAAYC,EAAc,aAC1B,GAAGC,CACL,EAAIH,GAAU,CACZ,eAAgB,SAChB,WAAY,GACZ,MAAO,OACP,QAAS,CAAC,CACZ,EAGA,GAAI,CAACC,EAAS,CACZ,IAAMG,EAAYD,EAAU,SAAW,CAAC,EACxCJ,EAAU,UAAY;AAAA;AAAA,YAEhBK,EACC,IACEC,GAAM;AAAA;AAAA,wBAEGA,EAAE,OAAO,QAAQ;AAAA,4DACmBA,EAAE,OAAO,MAAM;AAAA;AAAA,WAG/D,EACC,KAAK,EAAE,CAAC;AAAA;AAAA,OAGjB,CAEA,MAAO,IAAM,CACXN,EAAU,UAAY,EACxB,CACF,CACF,ECvaO,IAAMO,EAAU,CACrB,GAAI,eACJ,QAAS,QACT,KAAM,gBACN,YAAa,iEAMb,UAAW,CAAC,EAKZ,QAAS,CACP,CACE,GAAI,yBACJ,UAAWC,EACX,SAAU,CACR,KAAM,gBACN,YAAa,wCACb,KAAM,QACR,CACF,CACF,CACF,ECjCO,IAAMC,EAAW,CACtB,GAAI,MACJ,QAASC,EAAQ,QACjB,KAAMA,EAAQ,KACd,YAAaA,EAAQ,YACrB,QAAS,CAEP,QAAS,CAAC,EACV,QAASA,EAAQ,OACnB,EACA,SAAU,CACR,UAAW,EACb,CACF,EAMA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAMC,EAAkB,OAAe,uBACnCA,GAAkB,OAAOA,EAAe,UAAa,YACvDA,EAAe,SAASF,CAAQ,CAEpC,CAEA,IAAOG,EAAQH",
6
- "names": ["useEffect", "useReducer", "useMemo", "useCallback", "useState", "jsx", "jsxs", "FAQMountableWidget", "container", "config", "runtime", "_instanceId", "faqConfig", "questions", "q", "runtime", "FAQMountableWidget", "manifest", "runtime", "globalRegistry", "cdn_default"]
7
- }
@@ -1,2 +0,0 @@
1
- var c=async(e,t)=>{let{badgeId:i}=e;return t.publishEvent("gamification.badge_awarded",{badgeId:i,awardedAt:Date.now()}),{cleanup:()=>{}}},d=async(e,t)=>{let{points:i,reason:s}=e;return t.publishEvent("gamification.points_added",{points:i,reason:s,timestamp:Date.now()}),{cleanup:()=>{}}},g={names:["page_view","button_click"],handler:(e,t)=>{console.log("[Gamification] Event received for badge trigger check")}},o=[{kind:"gamification:awardBadge",executor:c},{kind:"gamification:addPoints",executor:d}],a=[g],n={id:"adaptive-gamification",version:"1.0.0",name:"Gamification",description:"Badges, rewards, points, and engagement mechanics",executors:o,eventHandlers:a};var r={id:"gamification",version:n.version,name:n.name,description:n.description,runtime:{actions:o.map(({kind:e,executor:t})=>({kind:e,executor:t})),events:a},metadata:{isBuiltIn:!1}};if(typeof window<"u"){let e=window.__SYNOS_APP_REGISTRY__;e&&typeof e.register=="function"&&e.register(r)}var p=r;export{p as default,r as manifest};
2
- //# sourceMappingURL=index.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../adaptives/adaptive-gamification/src/runtime.ts", "../../../../adaptives/adaptive-gamification/src/cdn.ts"],
4
- "sourcesContent": ["/**\n * Adaptive Gamification - Runtime Module\n *\n * Gamification actions: awardBadge, addPoints.\n * Provides gamification features like badges, points, and rewards.\n */\n\nimport type { ExecutorResult, ActionExecutor } from './types';\n\n// ============================================================================\n// Action Types\n// ============================================================================\n\n/**\n * Award badge action\n */\nexport interface AwardBadgeAction {\n kind: 'gamification:awardBadge';\n badgeId: string;\n anchorId?: string;\n label?: string;\n}\n\n/**\n * Add points action\n */\nexport interface AddPointsAction {\n kind: 'gamification:addPoints';\n points: number;\n reason?: string;\n label?: string;\n}\n\n// ============================================================================\n// Executors\n// ============================================================================\n\n/**\n * Execute an awardBadge action\n *\n * Note: This executor uses publishEvent to track badge awards.\n * State management is handled at the app level via AppContext,\n * not at the action executor level.\n */\nexport const executeAwardBadge: ActionExecutor<AwardBadgeAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n const { badgeId } = action;\n\n // Emit telemetry event (state management handled at app level)\n context.publishEvent('gamification.badge_awarded', {\n badgeId,\n awardedAt: Date.now(),\n });\n\n return {\n cleanup: () => {\n // Badge awards are permanent, no cleanup needed\n },\n };\n};\n\n/**\n * Execute an addPoints action\n *\n * Note: This executor uses publishEvent to track points.\n * State management is handled at the app level via AppContext,\n * not at the action executor level.\n */\nexport const executeAddPoints: ActionExecutor<AddPointsAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n const { points, reason } = action;\n\n // Emit telemetry event (state management handled at app level)\n context.publishEvent('gamification.points_added', {\n points,\n reason,\n timestamp: Date.now(),\n });\n\n return {\n cleanup: () => {\n // Points are permanent, no cleanup needed\n },\n };\n};\n\n// ============================================================================\n// Event Handlers\n// ============================================================================\n\n/**\n * Event handler for auto-awarding badges based on triggers.\n */\nexport const badgeTriggerHandler = {\n names: ['page_view', 'button_click'],\n handler: (_event: unknown, _ctx: unknown) => {\n // Auto-award badges based on event triggers\n // This would check badge trigger conditions in the config\n console.log('[Gamification] Event received for badge trigger check');\n },\n};\n\n// ============================================================================\n// Executor Definitions for Registration\n// ============================================================================\n\n/**\n * All executors provided by this app.\n * These are registered with the runtime's ExecutorRegistry.\n */\nexport const executors = [\n { kind: 'gamification:awardBadge', executor: executeAwardBadge },\n { kind: 'gamification:addPoints', executor: executeAddPoints },\n] as const;\n\n/**\n * Event handlers provided by this app.\n */\nexport const eventHandlers = [badgeTriggerHandler];\n\n/**\n * App runtime manifest.\n */\nexport const runtime = {\n id: 'adaptive-gamification',\n version: '1.0.0',\n name: 'Gamification',\n description: 'Badges, rewards, points, and engagement mechanics',\n executors,\n eventHandlers,\n};\n", "/**\n * CDN Entry Point for Adaptive Gamification\n *\n * This module is bundled for CDN delivery and self-registers with the global\n * SynOS app registry when loaded dynamically via the AppLoader.\n */\n\nimport { executors, eventHandlers, runtime } from './runtime';\n\n/**\n * App manifest for registry registration.\n * Follows the AppManifest interface expected by AppLoader/AppRegistry.\n */\nexport const manifest = {\n id: 'gamification',\n version: runtime.version,\n name: runtime.name,\n description: runtime.description,\n runtime: {\n actions: executors.map(({ kind, executor }) => ({\n kind,\n executor,\n })),\n events: eventHandlers,\n },\n metadata: {\n isBuiltIn: false,\n },\n};\n\n/**\n * Self-register with global registry if available.\n * This happens when loaded via script tag (UMD).\n */\nif (typeof window !== 'undefined') {\n const globalRegistry = (window as any).__SYNOS_APP_REGISTRY__;\n if (globalRegistry && typeof globalRegistry.register === 'function') {\n globalRegistry.register(manifest);\n }\n}\n\nexport default manifest;\n"],
5
- "mappings": "AA4CO,IAAMA,EAAsD,MACjEC,EACAC,IAC4B,CAC5B,GAAM,CAAE,QAAAC,CAAQ,EAAIF,EAGpB,OAAAC,EAAQ,aAAa,6BAA8B,CACjD,QAAAC,EACA,UAAW,KAAK,IAAI,CACtB,CAAC,EAEM,CACL,QAAS,IAAM,CAEf,CACF,CACF,EASaC,EAAoD,MAC/DH,EACAC,IAC4B,CAC5B,GAAM,CAAE,OAAAG,EAAQ,OAAAC,CAAO,EAAIL,EAG3B,OAAAC,EAAQ,aAAa,4BAA6B,CAChD,OAAAG,EACA,OAAAC,EACA,UAAW,KAAK,IAAI,CACtB,CAAC,EAEM,CACL,QAAS,IAAM,CAEf,CACF,CACF,EASaC,EAAsB,CACjC,MAAO,CAAC,YAAa,cAAc,EACnC,QAAS,CAACC,EAAiBC,IAAkB,CAG3C,QAAQ,IAAI,uDAAuD,CACrE,CACF,EAUaC,EAAY,CACvB,CAAE,KAAM,0BAA2B,SAAUV,CAAkB,EAC/D,CAAE,KAAM,yBAA0B,SAAUI,CAAiB,CAC/D,EAKaO,EAAgB,CAACJ,CAAmB,EAKpCK,EAAU,CACrB,GAAI,wBACJ,QAAS,QACT,KAAM,eACN,YAAa,oDACb,UAAAF,EACA,cAAAC,CACF,ECzHO,IAAME,EAAW,CACtB,GAAI,eACJ,QAASC,EAAQ,QACjB,KAAMA,EAAQ,KACd,YAAaA,EAAQ,YACrB,QAAS,CACP,QAASC,EAAU,IAAI,CAAC,CAAE,KAAAC,EAAM,SAAAC,CAAS,KAAO,CAC9C,KAAAD,EACA,SAAAC,CACF,EAAE,EACF,OAAQC,CACV,EACA,SAAU,CACR,UAAW,EACb,CACF,EAMA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAMC,EAAkB,OAAe,uBACnCA,GAAkB,OAAOA,EAAe,UAAa,YACvDA,EAAe,SAASN,CAAQ,CAEpC,CAEA,IAAOO,EAAQP",
6
- "names": ["executeAwardBadge", "action", "context", "badgeId", "executeAddPoints", "points", "reason", "badgeTriggerHandler", "_event", "_ctx", "executors", "eventHandlers", "runtime", "manifest", "runtime", "executors", "kind", "executor", "eventHandlers", "globalRegistry", "cdn_default"]
7
- }
@@ -1,11 +0,0 @@
1
- import p,{useEffect as f,useReducer as u,useMemo as v,useCallback as g}from"react";import{jsx as k,jsxs as b}from"react/jsx-runtime";var o={mount(e,i){let{runtime:r,instanceId:c="nav-widget",...s}=i||{layout:"horizontal",theme:"auto",actions:[]};if(!r){let l=s.actions||[];e.innerHTML=`
2
- <nav style="display: flex; gap: 8px; padding: 8px; font-family: system-ui;">
3
- ${l.map(t=>`
4
- <a href="${t.config.href}" style="padding: 8px 12px; text-decoration: none; color: #374151;">
5
- ${t.config.icon?`<span>${t.config.icon}</span>`:""}
6
- ${t.config.label}
7
- </a>
8
- `).join("")}
9
- </nav>
10
- `}return()=>{e.innerHTML=""}}};var n={id:"adaptive-nav",version:"1.0.0",name:"Navigation Links",description:"Widget-based navigation link list with per-item conditional visibility",executors:[],widgets:[{id:"adaptive-nav:links",component:o,metadata:{name:"Navigation Links",description:"Horizontal or vertical navigation link list",icon:"\u{1F517}"}}]};var a={id:"nav",version:n.version,name:n.name,description:n.description,runtime:{actions:[],widgets:n.widgets},metadata:{isBuiltIn:!1}};if(typeof window<"u"){let e=window.__SYNOS_APP_REGISTRY__;e&&typeof e.register=="function"&&e.register(a)}var N=a;export{N as default,a as manifest};
11
- //# sourceMappingURL=index.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../adaptives/adaptive-nav/src/NavWidget.tsx", "../../../../adaptives/adaptive-nav/src/runtime.ts", "../../../../adaptives/adaptive-nav/src/cdn.ts"],
4
- "sourcesContent": ["/**\n * Adaptive Nav - NavWidget Component\n *\n * React component that renders a navigation link list with per-item\n * conditional visibility based on showWhen decision strategies.\n *\n * Demonstrates the compositional action pattern where child actions\n * (nav:link) serve as configuration data for the parent widget.\n */\n\nimport React, { useEffect, useReducer, useMemo, useCallback } from 'react';\nimport type { NavWidgetProps, NavLinkAction, NavConfig, NavWidgetRuntime } from './types';\n\n// ============================================================================\n// Styles\n// ============================================================================\n\nconst baseStyles = {\n nav: {\n display: 'flex',\n gap: '4px',\n padding: '8px',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n link: {\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n padding: '8px 12px',\n borderRadius: '6px',\n textDecoration: 'none',\n fontSize: '14px',\n fontWeight: 500,\n transition: 'background-color 0.15s ease, color 0.15s ease',\n cursor: 'pointer',\n border: 'none',\n background: 'transparent',\n },\n icon: {\n fontSize: '16px',\n },\n externalIcon: {\n fontSize: '12px',\n opacity: 0.6,\n },\n} as const;\n\nconst themeStyles = {\n light: {\n nav: {\n backgroundColor: '#ffffff',\n },\n link: {\n color: '#374151',\n },\n linkHover: {\n backgroundColor: '#f3f4f6',\n color: '#111827',\n },\n },\n dark: {\n nav: {\n backgroundColor: '#1f2937',\n },\n link: {\n color: '#d1d5db',\n },\n linkHover: {\n backgroundColor: '#374151',\n color: '#f9fafb',\n },\n },\n} as const;\n\n// ============================================================================\n// NavLink Component\n// ============================================================================\n\ninterface NavLinkComponentProps {\n link: NavLinkAction;\n theme: 'light' | 'dark';\n onNavigate: (href: string, external: boolean) => void;\n}\n\nfunction NavLinkComponent({ link, theme, onNavigate }: NavLinkComponentProps) {\n const [isHovered, setIsHovered] = React.useState(false);\n const { label, href, icon, external } = link.config;\n const colors = themeStyles[theme];\n\n const style: React.CSSProperties = {\n ...baseStyles.link,\n ...colors.link,\n ...(isHovered ? colors.linkHover : {}),\n };\n\n const handleClick = (e: React.MouseEvent) => {\n e.preventDefault();\n onNavigate(href, external ?? false);\n };\n\n return (\n <a\n href={href}\n onClick={handleClick}\n style={style}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n target={external ? '_blank' : undefined}\n rel={external ? 'noopener noreferrer' : undefined}\n >\n {icon && <span style={baseStyles.icon}>{icon}</span>}\n <span>{label}</span>\n {external && <span style={baseStyles.externalIcon}>\u2197</span>}\n </a>\n );\n}\n\n// ============================================================================\n// NavWidget Component\n// ============================================================================\n\n/**\n * NavWidget - Renders a navigation link list with per-item activation.\n *\n * This component demonstrates the compositional action pattern:\n * - Parent (NavWidget) receives `config.actions` array\n * - Each action has optional `showWhen` for per-item visibility\n * - Parent evaluates showWhen and filters visible links\n * - Parent manages re-rendering on context changes\n */\nexport function NavWidget({ config, runtime, instanceId }: NavWidgetProps) {\n // Force re-render when context changes\n const [, forceUpdate] = useReducer((x) => x + 1, 0);\n\n // Subscribe to context changes for reactive updates\n useEffect(() => {\n const unsubscribe = runtime.context.subscribe(() => {\n forceUpdate();\n });\n return unsubscribe;\n }, [runtime.context]);\n\n // Filter visible links based on per-item showWhen\n const visibleLinks = useMemo(() => {\n return config.actions.filter((link) => {\n // No showWhen = always visible\n if (!link.showWhen) return true;\n\n // Evaluate the decision strategy\n const result = runtime.evaluateSync<boolean>(link.showWhen);\n return result.value;\n });\n }, [config.actions, runtime]);\n\n // Resolve theme (auto \u2192 detect system preference)\n const resolvedTheme = useMemo(() => {\n if (config.theme !== 'auto') return config.theme;\n\n // Check system preference (SSR-safe)\n if (typeof window !== 'undefined') {\n return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n return 'light';\n }, [config.theme]);\n\n // Handle navigation with event publishing\n const handleNavigate = useCallback(\n (href: string, external: boolean) => {\n // Publish navigation event for analytics\n runtime.events.publish('nav:click', {\n instanceId,\n href,\n external,\n timestamp: Date.now(),\n });\n\n // Perform navigation\n if (external) {\n window.open(href, '_blank', 'noopener,noreferrer');\n } else {\n window.location.href = href;\n }\n },\n [runtime.events, instanceId]\n );\n\n // Compute nav styles\n const navStyle: React.CSSProperties = {\n ...baseStyles.nav,\n ...themeStyles[resolvedTheme].nav,\n flexDirection: config.layout === 'vertical' ? 'column' : 'row',\n };\n\n // Empty state\n if (visibleLinks.length === 0) {\n return null;\n }\n\n return (\n <nav style={navStyle} data-adaptive-id={instanceId} data-adaptive-type=\"adaptive-nav\">\n {visibleLinks.map((link, index) => (\n <NavLinkComponent\n key={link.config.href + index}\n link={link}\n theme={resolvedTheme}\n onNavigate={handleNavigate}\n />\n ))}\n </nav>\n );\n}\n\n// ============================================================================\n// Mountable Widget Interface\n// ============================================================================\n\n/**\n * Mountable widget interface for the runtime's WidgetRegistry.\n */\nexport const NavMountableWidget = {\n mount(\n container: HTMLElement,\n config?: NavConfig & { runtime?: NavWidgetRuntime; instanceId?: string }\n ) {\n // This is a simplified mount for non-React environments\n // In practice, the runtime handles React rendering\n\n const {\n runtime,\n instanceId: _instanceId = 'nav-widget',\n ...navConfig\n } = config || {\n layout: 'horizontal' as const,\n theme: 'auto' as const,\n actions: [],\n };\n\n // Create simple HTML fallback if no runtime\n if (!runtime) {\n const links = navConfig.actions || [];\n container.innerHTML = `\n <nav style=\"display: flex; gap: 8px; padding: 8px; font-family: system-ui;\">\n ${links\n .map(\n (link) => `\n <a href=\"${link.config.href}\" style=\"padding: 8px 12px; text-decoration: none; color: #374151;\">\n ${link.config.icon ? `<span>${link.config.icon}</span>` : ''}\n ${link.config.label}\n </a>\n `\n )\n .join('')}\n </nav>\n `;\n }\n\n return () => {\n container.innerHTML = '';\n };\n },\n};\n\nexport default NavWidget;\n", "/**\n * Adaptive Nav - Runtime Module\n *\n * Runtime manifest for the navigation link list adaptive.\n * This is a widget-based adaptive with no action executors.\n */\n\nimport { NavMountableWidget } from './NavWidget';\n\n// ============================================================================\n// App Runtime Manifest\n// ============================================================================\n\n/**\n * Runtime manifest for adaptive-nav.\n *\n * Note: This adaptive is widget-based, not action-based.\n * The `nav:link` actions are compositional - they're rendered by\n * the widget, not executed by the runtime.\n */\nexport const runtime = {\n id: 'adaptive-nav',\n version: '1.0.0',\n name: 'Navigation Links',\n description: 'Widget-based navigation link list with per-item conditional visibility',\n\n /**\n * No action executors - nav:link actions are compositional,\n * meaning they serve as configuration for the NavWidget.\n */\n executors: [],\n\n /**\n * Widget definitions for the runtime's WidgetRegistry.\n */\n widgets: [\n {\n id: 'adaptive-nav:links',\n component: NavMountableWidget,\n metadata: {\n name: 'Navigation Links',\n description: 'Horizontal or vertical navigation link list',\n icon: '\uD83D\uDD17',\n },\n },\n ],\n};\n\nexport default runtime;\n", "/**\n * CDN Entry Point for Adaptive Nav\n *\n * This module is bundled for CDN delivery and self-registers with the global\n * SynOS app registry when loaded dynamically via the AppLoader.\n */\n\nimport { runtime } from './runtime';\n\n/**\n * App manifest for registry registration.\n * Follows the AppManifest interface expected by AppLoader/AppRegistry.\n */\nexport const manifest = {\n id: 'nav',\n version: runtime.version,\n name: runtime.name,\n description: runtime.description,\n runtime: {\n // Nav is widget-based, no action executors\n actions: [],\n widgets: runtime.widgets,\n },\n metadata: {\n isBuiltIn: false,\n },\n};\n\n/**\n * Self-register with global registry if available.\n * This happens when loaded via script tag (UMD).\n */\nif (typeof window !== 'undefined') {\n const globalRegistry = (window as any).__SYNOS_APP_REGISTRY__;\n if (globalRegistry && typeof globalRegistry.register === 'function') {\n globalRegistry.register(manifest);\n }\n}\n\nexport default manifest;\n"],
5
- "mappings": "AAUA,OAAOA,GAAS,aAAAC,EAAW,cAAAC,EAAY,WAAAC,EAAS,eAAAC,MAAmB,QA2F/D,OASW,OAAAC,EATX,QAAAC,MAAA,oBAsHG,IAAMC,EAAqB,CAChC,MACEC,EACAC,EACA,CAIA,GAAM,CACJ,QAAAC,EACA,WAAYC,EAAc,aAC1B,GAAGC,CACL,EAAIH,GAAU,CACZ,OAAQ,aACR,MAAO,OACP,QAAS,CAAC,CACZ,EAGA,GAAI,CAACC,EAAS,CACZ,IAAMG,EAAQD,EAAU,SAAW,CAAC,EACpCJ,EAAU,UAAY;AAAA;AAAA,YAEhBK,EACC,IACEC,GAAS;AAAA,uBACDA,EAAK,OAAO,IAAI;AAAA,gBACvBA,EAAK,OAAO,KAAO,SAASA,EAAK,OAAO,IAAI,UAAY,EAAE;AAAA,gBAC1DA,EAAK,OAAO,KAAK;AAAA;AAAA,WAGrB,EACC,KAAK,EAAE,CAAC;AAAA;AAAA,OAGjB,CAEA,MAAO,IAAM,CACXN,EAAU,UAAY,EACxB,CACF,CACF,EChPO,IAAMO,EAAU,CACrB,GAAI,eACJ,QAAS,QACT,KAAM,mBACN,YAAa,yEAMb,UAAW,CAAC,EAKZ,QAAS,CACP,CACE,GAAI,qBACJ,UAAWC,EACX,SAAU,CACR,KAAM,mBACN,YAAa,8CACb,KAAM,WACR,CACF,CACF,CACF,ECjCO,IAAMC,EAAW,CACtB,GAAI,MACJ,QAASC,EAAQ,QACjB,KAAMA,EAAQ,KACd,YAAaA,EAAQ,YACrB,QAAS,CAEP,QAAS,CAAC,EACV,QAASA,EAAQ,OACnB,EACA,SAAU,CACR,UAAW,EACb,CACF,EAMA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAMC,EAAkB,OAAe,uBACnCA,GAAkB,OAAOA,EAAe,UAAa,YACvDA,EAAe,SAASF,CAAQ,CAEpC,CAEA,IAAOG,EAAQH",
6
- "names": ["React", "useEffect", "useReducer", "useMemo", "useCallback", "jsx", "jsxs", "NavMountableWidget", "container", "config", "runtime", "_instanceId", "navConfig", "links", "link", "runtime", "NavMountableWidget", "manifest", "runtime", "globalRegistry", "cdn_default"]
7
- }