@syntrologie/runtime-sdk 1.0.1 → 2.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 (128) hide show
  1. package/CAPABILITIES.md +630 -463
  2. package/README.md +285 -62
  3. package/dist/RuntimeProvider.d.ts +51 -0
  4. package/dist/RuntimeProvider.js +114 -0
  5. package/dist/RuntimeProvider.js.map +1 -0
  6. package/dist/SmartCanvasApp.d.ts +9 -3
  7. package/dist/SmartCanvasApp.js +36 -38
  8. package/dist/SmartCanvasApp.js.map +1 -1
  9. package/dist/actions/ActionEngine.d.ts +11 -0
  10. package/dist/actions/ActionEngine.js +274 -0
  11. package/dist/actions/ActionEngine.js.map +1 -0
  12. package/dist/actions/executors/index.d.ts +117 -0
  13. package/dist/actions/executors/index.js +242 -0
  14. package/dist/actions/executors/index.js.map +1 -0
  15. package/dist/actions/executors/tour.d.ts +18 -0
  16. package/dist/actions/executors/tour.js +332 -0
  17. package/dist/actions/executors/tour.js.map +1 -0
  18. package/dist/actions/index.d.ts +10 -0
  19. package/dist/actions/index.js +12 -0
  20. package/dist/actions/index.js.map +1 -0
  21. package/dist/actions/types.d.ts +399 -0
  22. package/dist/actions/types.js +8 -0
  23. package/dist/actions/types.js.map +1 -0
  24. package/dist/actions/validation.d.ts +14 -0
  25. package/dist/actions/validation.js +603 -0
  26. package/dist/actions/validation.js.map +1 -0
  27. package/dist/api.d.ts +32 -18
  28. package/dist/api.js +56 -39
  29. package/dist/api.js.map +1 -1
  30. package/dist/apps/AppContext.d.ts +31 -0
  31. package/dist/apps/AppContext.js +93 -0
  32. package/dist/apps/AppContext.js.map +1 -0
  33. package/dist/apps/AppLoader.d.ts +84 -0
  34. package/dist/apps/AppLoader.js +256 -0
  35. package/dist/apps/AppLoader.js.map +1 -0
  36. package/dist/apps/AppRegistry.d.ts +102 -0
  37. package/dist/apps/AppRegistry.js +317 -0
  38. package/dist/apps/AppRegistry.js.map +1 -0
  39. package/dist/apps/examples/gamification-app.example.d.ts +305 -0
  40. package/dist/apps/examples/gamification-app.example.js +329 -0
  41. package/dist/apps/examples/gamification-app.example.js.map +1 -0
  42. package/dist/apps/faq/index.js +11 -0
  43. package/dist/apps/faq/index.js.map +7 -0
  44. package/dist/apps/gamification/index.js +2 -0
  45. package/dist/apps/gamification/index.js.map +7 -0
  46. package/dist/apps/index.d.ts +15 -0
  47. package/dist/apps/index.js +17 -0
  48. package/dist/apps/index.js.map +1 -0
  49. package/dist/apps/nav/index.js +11 -0
  50. package/dist/apps/nav/index.js.map +7 -0
  51. package/dist/apps/types.d.ts +231 -0
  52. package/dist/apps/types.js +8 -0
  53. package/dist/apps/types.js.map +1 -0
  54. package/dist/bootstrap.d.ts +24 -0
  55. package/dist/bootstrap.js +133 -33
  56. package/dist/bootstrap.js.map +1 -1
  57. package/dist/components/ShadowCanvasOverlay.js +36 -9
  58. package/dist/components/ShadowCanvasOverlay.js.map +1 -1
  59. package/dist/components/TileCard.js +37 -18
  60. package/dist/components/TileCard.js.map +1 -1
  61. package/dist/context/schema.d.ts +16 -16
  62. package/dist/decisions/schema.d.ts +96 -96
  63. package/dist/earlyPatcher.d.ts +8 -20
  64. package/dist/earlyPatcher.js +13 -62
  65. package/dist/earlyPatcher.js.map +1 -1
  66. package/dist/editorLoader.d.ts +2 -0
  67. package/dist/editorLoader.js +52 -7
  68. package/dist/editorLoader.js.map +1 -1
  69. package/dist/events/normalizers/posthog.d.ts +24 -0
  70. package/dist/events/normalizers/posthog.js.map +1 -1
  71. package/dist/events/schema.d.ts +8 -8
  72. package/dist/events/types.d.ts +6 -0
  73. package/dist/events/types.js +8 -0
  74. package/dist/events/types.js.map +1 -1
  75. package/dist/hooks/useCanvasOverlays.d.ts +4 -1
  76. package/dist/hooks/useCanvasOverlays.js +53 -6
  77. package/dist/hooks/useCanvasOverlays.js.map +1 -1
  78. package/dist/hooks/useShadowCanvasConfig.d.ts +3 -7
  79. package/dist/hooks/useShadowCanvasConfig.js +2 -3
  80. package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
  81. package/dist/index.d.ts +5 -0
  82. package/dist/index.js +10 -0
  83. package/dist/index.js.map +1 -1
  84. package/dist/overlays/schema.d.ts +153 -153
  85. package/dist/runtime.d.ts +24 -0
  86. package/dist/runtime.js +75 -1
  87. package/dist/runtime.js.map +1 -1
  88. package/dist/smart-canvas.esm.js +155 -78
  89. package/dist/smart-canvas.esm.js.map +4 -4
  90. package/dist/smart-canvas.js +41155 -35362
  91. package/dist/smart-canvas.js.map +4 -4
  92. package/dist/smart-canvas.min.js +155 -78
  93. package/dist/smart-canvas.min.js.map +4 -4
  94. package/dist/store/example.d.ts +1 -0
  95. package/dist/store/example.js +43 -0
  96. package/dist/store/example.js.map +1 -0
  97. package/dist/store/mini-effector.d.ts +46 -0
  98. package/dist/store/mini-effector.js +90 -0
  99. package/dist/store/mini-effector.js.map +1 -0
  100. package/dist/surfaces/Surfaces.d.ts +11 -0
  101. package/dist/surfaces/Surfaces.js +361 -0
  102. package/dist/surfaces/Surfaces.js.map +1 -0
  103. package/dist/surfaces/index.d.ts +9 -0
  104. package/dist/surfaces/index.js +12 -0
  105. package/dist/surfaces/index.js.map +1 -0
  106. package/dist/surfaces/positioning.d.ts +50 -0
  107. package/dist/surfaces/positioning.js +231 -0
  108. package/dist/surfaces/positioning.js.map +1 -0
  109. package/dist/surfaces/types.d.ts +167 -0
  110. package/dist/surfaces/types.js +23 -0
  111. package/dist/surfaces/types.js.map +1 -0
  112. package/dist/telemetry/adapters/posthog.d.ts +6 -0
  113. package/dist/telemetry/adapters/posthog.js +9 -0
  114. package/dist/telemetry/adapters/posthog.js.map +1 -1
  115. package/dist/types-only.d.ts +32 -0
  116. package/dist/types-only.js +11 -0
  117. package/dist/types-only.js.map +1 -0
  118. package/dist/types.d.ts +26 -14
  119. package/dist/types.js +1 -1
  120. package/dist/types.js.map +1 -1
  121. package/dist/widgets/WidgetRegistry.d.ts +139 -0
  122. package/dist/widgets/WidgetRegistry.js +182 -0
  123. package/dist/widgets/WidgetRegistry.js.map +1 -0
  124. package/dist/widgets/index.d.ts +7 -0
  125. package/dist/widgets/index.js +7 -0
  126. package/dist/widgets/index.js.map +1 -0
  127. package/package.json +14 -7
  128. package/schema/canvas-config.schema.json +444 -254
@@ -0,0 +1,7 @@
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\n ? \"dark\"\n : \"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}>\n No FAQ questions available.\n </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 { runtime, instanceId = \"faq-widget\", ...faqConfig } = 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,oBAmMC,IAAMC,EAAqB,CAChC,MACEC,EACAC,EACA,CAIA,GAAM,CAAE,QAAAC,EAAS,WAAAC,EAAa,aAAc,GAAGC,CAAU,EAAIH,GAAU,CACrE,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
+ }
@@ -0,0 +1,2 @@
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
@@ -0,0 +1,7 @@
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 {\n ExecutorResult,\n ExecutorContext,\n ActionExecutor,\n} 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": "AAgDO,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,EAAgBC,IAAiB,CAGzC,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,EC7HO,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
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Apps Module
3
+ *
4
+ * SynOS app system for extensible runtime and editor modules.
5
+ */
6
+ export { AppRegistry, appRegistry } from "./AppRegistry";
7
+ export type { AppRegistryEventCallback } from "./AppRegistry";
8
+ export { createAppContext, cleanupAppContext } from "./AppContext";
9
+ export type { CreateAppContextOptions } from "./AppContext";
10
+ export { createAppLoader, isCoreActionKind, getAppIdFromActionKind, getAppIdFromWidgetId, } from "./AppLoader";
11
+ export type { AppLoader, AppLoaderOptions, AppLoadResult, CanvasConfigForLoader, } from "./AppLoader";
12
+ export { runtime as contentRuntime } from "@syntrologie/app-content/runtime";
13
+ export { runtime as overlaysRuntime } from "@syntrologie/app-overlays/runtime";
14
+ export { runtime as navigationRuntime } from "@syntrologie/app-navigation/runtime";
15
+ export type { AppManifest, AppRegistration, AppLifecycleState, AppContext, AppRuntimeExtensions, AppActionDefinition, AppWidgetDefinition, AppEventHandler, AppEventContext, EditorPanelProps, EditorPanelConfig, AppEditorModule, AppRegistryEvent, } from "./types";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Apps Module
3
+ *
4
+ * SynOS app system for extensible runtime and editor modules.
5
+ */
6
+ // App Registry
7
+ export { AppRegistry, appRegistry } from "./AppRegistry";
8
+ // App Context
9
+ export { createAppContext, cleanupAppContext } from "./AppContext";
10
+ // App Loader
11
+ export { createAppLoader, isCoreActionKind, getAppIdFromActionKind, getAppIdFromWidgetId, } from "./AppLoader";
12
+ // Core app runtime modules (bundled with runtime SDK)
13
+ // These executors are available immediately without CDN loading.
14
+ export { runtime as contentRuntime } from "@syntrologie/app-content/runtime";
15
+ export { runtime as overlaysRuntime } from "@syntrologie/app-overlays/runtime";
16
+ export { runtime as navigationRuntime } from "@syntrologie/app-navigation/runtime";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/apps/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGzD,cAAc;AACd,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGnE,aAAa;AACb,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAQrB,sDAAsD;AACtD,iEAAiE;AACjE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qCAAqC,CAAC"}
@@ -0,0 +1,11 @@
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
@@ -0,0 +1,7 @@
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\n ? \"dark\"\n : \"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 { runtime, instanceId = \"nav-widget\", ...navConfig } = 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,oBAwHG,IAAMC,EAAqB,CAChC,MACEC,EACAC,EACA,CAIA,GAAM,CAAE,QAAAC,EAAS,WAAAC,EAAa,aAAc,GAAGC,CAAU,EAAIH,GAAU,CACrE,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,EC9OO,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
+ }
@@ -0,0 +1,231 @@
1
+ /**
2
+ * App Types
3
+ *
4
+ * Defines the types for the SynOS app system.
5
+ * Apps provide runtime extensions (actions, widgets, events) and editor modules.
6
+ */
7
+ import type { ZodSchema } from "zod";
8
+ import type { ActionExecutor } from "../actions/types";
9
+ import type { MountableWidget } from "../widgets/WidgetRegistry";
10
+ import type { NormalizedEvent } from "../events/types";
11
+ /**
12
+ * Action definition provided by an app.
13
+ */
14
+ export interface AppActionDefinition {
15
+ /** Action kind (e.g., "gamification:awardBadge") */
16
+ kind: string;
17
+ /** Zod schema for validation */
18
+ schema?: ZodSchema;
19
+ /** Executor function */
20
+ executor: ActionExecutor<any>;
21
+ }
22
+ /**
23
+ * Widget definition provided by an app.
24
+ */
25
+ export interface AppWidgetDefinition {
26
+ /** Widget ID (e.g., "gamification:leaderboard") */
27
+ id: string;
28
+ /** The widget component */
29
+ component: MountableWidget;
30
+ /** Optional metadata */
31
+ metadata?: {
32
+ name?: string;
33
+ description?: string;
34
+ icon?: string;
35
+ };
36
+ }
37
+ /**
38
+ * Context passed to app event handlers.
39
+ */
40
+ export interface AppEventContext {
41
+ /** The app ID */
42
+ appId: string;
43
+ /** Access to state store */
44
+ state: {
45
+ get: (key: string) => unknown;
46
+ set: (key: string, value: unknown) => void;
47
+ };
48
+ /** Emit telemetry events */
49
+ telemetry?: {
50
+ emit: (eventName: string, props?: Record<string, unknown>) => void;
51
+ };
52
+ /** Access to surfaces */
53
+ surfaces?: {
54
+ mount: (slot: string, content: unknown, opts?: unknown) => {
55
+ unmount: () => void;
56
+ };
57
+ };
58
+ }
59
+ /**
60
+ * Event handler definition provided by an app.
61
+ */
62
+ export interface AppEventHandler {
63
+ /** Event names to listen for */
64
+ names: string[];
65
+ /** Handler function */
66
+ handler: (event: NormalizedEvent, ctx: AppEventContext) => void | Promise<void>;
67
+ }
68
+ /**
69
+ * Runtime extensions provided by an app.
70
+ */
71
+ export interface AppRuntimeExtensions {
72
+ /** Custom action kinds */
73
+ actions?: AppActionDefinition[];
74
+ /** Widget components */
75
+ widgets?: AppWidgetDefinition[];
76
+ /** Event handlers */
77
+ events?: AppEventHandler[];
78
+ }
79
+ /**
80
+ * Props passed to editor panel components.
81
+ */
82
+ export interface EditorPanelProps {
83
+ /** The current config for this app's portion */
84
+ config: Record<string, unknown>;
85
+ /** Callback when config changes */
86
+ onChange: (config: Record<string, unknown>) => void;
87
+ /** Editor utilities */
88
+ editor: {
89
+ /** Mark the editor as dirty (has unsaved changes) */
90
+ setDirty: (dirty: boolean) => void;
91
+ /** Navigate back to home */
92
+ navigateHome: () => Promise<boolean>;
93
+ /** Save the current config */
94
+ save: () => Promise<void>;
95
+ /** Publish the current config */
96
+ publish: (captureScreenshot?: boolean) => Promise<void>;
97
+ };
98
+ /** Platform client for API calls (optional) */
99
+ platformClient?: unknown;
100
+ }
101
+ /**
102
+ * Editor panel configuration.
103
+ */
104
+ export interface EditorPanelConfig {
105
+ /** Panel title */
106
+ title: string;
107
+ /** Panel icon (emoji or icon key) */
108
+ icon: string;
109
+ /** Panel description */
110
+ description: string;
111
+ }
112
+ /**
113
+ * Editor module provided by an app.
114
+ */
115
+ export interface AppEditorModule {
116
+ /** React component for the editor panel */
117
+ component: React.ComponentType<EditorPanelProps>;
118
+ /** Panel configuration */
119
+ panel: EditorPanelConfig;
120
+ }
121
+ /**
122
+ * App manifest - the complete definition of a SynOS app.
123
+ */
124
+ export interface AppManifest {
125
+ /** Unique app identifier (e.g., "adaptive-gamification") */
126
+ id: string;
127
+ /** App version (semver) */
128
+ version: string;
129
+ /** Human-readable app name */
130
+ name: string;
131
+ /** App description */
132
+ description?: string;
133
+ /** Runtime extensions */
134
+ runtime?: AppRuntimeExtensions;
135
+ /** Zod schema for this app's config portion */
136
+ configSchema?: ZodSchema;
137
+ /** Editor module (optional - apps without editors are runtime-only) */
138
+ editor?: AppEditorModule;
139
+ /** App dependencies (other app IDs that must be loaded first) */
140
+ dependencies?: string[];
141
+ /** App metadata */
142
+ metadata?: {
143
+ /** Author name or organization */
144
+ author?: string;
145
+ /** Homepage URL */
146
+ homepage?: string;
147
+ /** License identifier */
148
+ license?: string;
149
+ /** Tags for categorization */
150
+ tags?: string[];
151
+ /** Whether this is a built-in app (bundled with runtime) */
152
+ isBuiltIn?: boolean;
153
+ };
154
+ }
155
+ /**
156
+ * App lifecycle state.
157
+ */
158
+ export type AppLifecycleState = "registered" | "activating" | "active" | "deactivating" | "inactive" | "error";
159
+ /**
160
+ * App registration - tracked state for a registered app.
161
+ */
162
+ export interface AppRegistration {
163
+ /** The app manifest */
164
+ manifest: AppManifest;
165
+ /** Current lifecycle state */
166
+ state: AppLifecycleState;
167
+ /** When the app was registered */
168
+ registeredAt: number;
169
+ /** When the app was last activated */
170
+ activatedAt?: number;
171
+ /** Error message if state is "error" */
172
+ error?: string;
173
+ /** Cleanup function to deactivate the app */
174
+ cleanup?: () => void | Promise<void>;
175
+ }
176
+ /**
177
+ * Sandboxed runtime context provided to apps.
178
+ */
179
+ export interface AppContext {
180
+ /** The app ID */
181
+ appId: string;
182
+ /** State store (scoped to app namespace) */
183
+ state: {
184
+ get: (key: string) => unknown;
185
+ set: (key: string, value: unknown) => void;
186
+ remove: (key: string) => void;
187
+ };
188
+ /** Telemetry (if available) */
189
+ telemetry?: {
190
+ emit: (eventName: string, props?: Record<string, unknown>) => void;
191
+ getSessionId: () => string | undefined;
192
+ };
193
+ /** Surfaces access */
194
+ surfaces: {
195
+ mount: (slot: string, content: unknown, opts?: unknown) => {
196
+ unmount: () => void;
197
+ update: (content: unknown) => void;
198
+ };
199
+ canMount: (slot: string, priority?: number) => boolean;
200
+ };
201
+ /** Event subscription */
202
+ events: {
203
+ subscribe: (callback: (event: NormalizedEvent) => void) => () => void;
204
+ publish: (name: string, props?: Record<string, unknown>) => void;
205
+ };
206
+ /** Register app-provided actions */
207
+ registerAction: (definition: AppActionDefinition) => void;
208
+ /** Register app-provided widgets */
209
+ registerWidget: (definition: AppWidgetDefinition) => void;
210
+ }
211
+ /**
212
+ * Events emitted by the app registry.
213
+ */
214
+ export type AppRegistryEvent = {
215
+ type: "app:registered";
216
+ appId: string;
217
+ manifest: AppManifest;
218
+ } | {
219
+ type: "app:activated";
220
+ appId: string;
221
+ } | {
222
+ type: "app:deactivated";
223
+ appId: string;
224
+ } | {
225
+ type: "app:error";
226
+ appId: string;
227
+ error: string;
228
+ } | {
229
+ type: "app:unregistered";
230
+ appId: string;
231
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * App Types
3
+ *
4
+ * Defines the types for the SynOS app system.
5
+ * Apps provide runtime extensions (actions, widgets, events) and editor modules.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/apps/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -20,6 +20,8 @@ import { decodeToken, encodeToken } from "./token";
20
20
  import { SmartCanvasConfig, SmartCanvasHandle } from "./api";
21
21
  import { SessionMetricTracker } from "./metrics";
22
22
  import { SmartCanvasRuntime } from "./runtime";
23
+ import { appRegistry } from "./apps";
24
+ import { type AppLoader } from "./apps/AppLoader";
23
25
  import type { ExperimentClient } from "./experiments/types";
24
26
  import type { TelemetryClient } from "./telemetry/types";
25
27
  import type { CanvasConfigFetcher } from "./types";
@@ -51,6 +53,18 @@ export interface SyntroInitOptions {
51
53
  * @default false
52
54
  */
53
55
  enableSessionMetrics?: boolean;
56
+ /**
57
+ * CDN base URL for loading external SynOS apps.
58
+ * Apps are loaded from: {cdnBase}/apps/{appId}/index.js
59
+ *
60
+ * @default "https://cdn.syntro.io"
61
+ */
62
+ cdnBase?: string;
63
+ /**
64
+ * @deprecated Core app executors are now registered synchronously.
65
+ * This option has no effect and will be removed in a future version.
66
+ */
67
+ registerBuiltInApps?: boolean;
54
68
  }
55
69
  export interface SyntroInitResult {
56
70
  /**
@@ -77,6 +91,11 @@ export interface SyntroInitResult {
77
91
  * @deprecated Access via runtime.sessionMetrics instead
78
92
  */
79
93
  sessionMetrics?: SessionMetricTracker;
94
+ /**
95
+ * App loader for loading external apps from CDN.
96
+ * Exposed for manual app loading if needed.
97
+ */
98
+ appLoader: AppLoader;
80
99
  }
81
100
  /**
82
101
  * Initialize the Syntro SDK with a single token.
@@ -103,6 +122,11 @@ export declare const Syntro: {
103
122
  declare global {
104
123
  interface Window {
105
124
  Syntro?: typeof Syntro;
125
+ SynOS?: {
126
+ appRegistry: typeof appRegistry;
127
+ runtime: SmartCanvasRuntime;
128
+ version: string;
129
+ };
106
130
  }
107
131
  }
108
132
  export {};