iobroker.mywebui 1.173.1 → 1.179.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 (265) hide show
  1. package/README.md +24 -0
  2. package/admin/i18n.json +42 -0
  3. package/admin/jsonConfig.json +35 -1
  4. package/default-controls/controls/dashboard/boiler.control +51 -0
  5. package/default-controls/controls/dashboard/command-group.control +19 -0
  6. package/default-controls/controls/dashboard/conveyor-box.control +19 -0
  7. package/default-controls/controls/dashboard/conveyor.control +35 -0
  8. package/default-controls/controls/dashboard/countdown-tile.control +32 -0
  9. package/default-controls/controls/dashboard/dial-gauge.control +39 -0
  10. package/default-controls/controls/dashboard/kpi-tile.control +52 -0
  11. package/default-controls/controls/dashboard/logic-gate.control +40 -0
  12. package/default-controls/controls/dashboard/notification-bell.control +19 -0
  13. package/default-controls/controls/dashboard/panel-card.control +45 -0
  14. package/default-controls/controls/dashboard/setpoint-slider.control +47 -0
  15. package/default-controls/controls/dashboard/status-chip.control +74 -0
  16. package/default-controls/controls/dashboard/time-spinner.control +37 -0
  17. package/default-controls/controls/navigation/app-bar.control +1 -1
  18. package/default-controls/controls/navigation/sidebar.control +1 -1
  19. package/dist/backend/3d-editor-integration.js +1 -1
  20. package/dist/backend/main.js +1 -1
  21. package/dist/backend/protectedWrite.js +1 -1
  22. package/dist/backend/reportCsv.js +1 -1
  23. package/dist/backend/reportGenerate.js +1 -1
  24. package/dist/backend/reportOutput.js +1 -1
  25. package/dist/backend/reportPdf.js +1 -1
  26. package/dist/backend/reportRenderer.js +1 -1
  27. package/dist/backend/reportSandbox.js +1 -1
  28. package/dist/backend/reportSandboxWorker.js +1 -1
  29. package/docs/README.md +1 -0
  30. package/docs/bindings.md +4 -0
  31. package/docs/reports.md +7 -0
  32. package/docs/scada-controls.md +47 -0
  33. package/docs/screens.md +13 -2
  34. package/io-package.json +1 -1
  35. package/native/index.js +2 -1
  36. package/native/mywebui_scada_engine.darwin-arm64.node +0 -0
  37. package/native/mywebui_scada_engine.darwin-x64.node +0 -0
  38. package/native/mywebui_scada_engine.linux-arm-gnueabihf.node +0 -0
  39. package/native/mywebui_scada_engine.linux-arm64-gnu.node +0 -0
  40. package/native/mywebui_scada_engine.linux-arm64-musl.node +0 -0
  41. package/native/mywebui_scada_engine.linux-x64-gnu.node +0 -0
  42. package/native/mywebui_scada_engine.linux-x64-musl.node +0 -0
  43. package/native/mywebui_scada_engine.win32-x64-msvc.node +0 -0
  44. package/package.json +1 -1
  45. package/www/3d-editor/js/Animation.js +1 -1
  46. package/www/3d-editor/js/AnimationResizer.js +1 -1
  47. package/www/3d-editor/js/Command.js +1 -1
  48. package/www/3d-editor/js/Config.js +1 -1
  49. package/www/3d-editor/js/Editor.js +1 -1
  50. package/www/3d-editor/js/EditorControls.js +1 -1
  51. package/www/3d-editor/js/GLTFImportDialog.js +1 -1
  52. package/www/3d-editor/js/History.js +1 -1
  53. package/www/3d-editor/js/Loader.js +1 -1
  54. package/www/3d-editor/js/LoaderUtils.js +1 -1
  55. package/www/3d-editor/js/Menubar.Add.js +1 -1
  56. package/www/3d-editor/js/Menubar.Edit.js +1 -1
  57. package/www/3d-editor/js/Menubar.File.js +1 -1
  58. package/www/3d-editor/js/Menubar.Help.js +1 -1
  59. package/www/3d-editor/js/Menubar.Render.js +1 -1
  60. package/www/3d-editor/js/Menubar.Status.js +1 -1
  61. package/www/3d-editor/js/Menubar.View.js +1 -1
  62. package/www/3d-editor/js/Menubar.js +1 -1
  63. package/www/3d-editor/js/Player.js +1 -1
  64. package/www/3d-editor/js/Resizer.js +1 -1
  65. package/www/3d-editor/js/Script.js +1 -1
  66. package/www/3d-editor/js/Selector.js +1 -1
  67. package/www/3d-editor/js/Sidebar.Geometry.BoxGeometry.js +1 -1
  68. package/www/3d-editor/js/Sidebar.Geometry.BufferGeometry.js +1 -1
  69. package/www/3d-editor/js/Sidebar.Geometry.CapsuleGeometry.js +1 -1
  70. package/www/3d-editor/js/Sidebar.Geometry.CircleGeometry.js +1 -1
  71. package/www/3d-editor/js/Sidebar.Geometry.CylinderGeometry.js +1 -1
  72. package/www/3d-editor/js/Sidebar.Geometry.DodecahedronGeometry.js +1 -1
  73. package/www/3d-editor/js/Sidebar.Geometry.ExtrudeGeometry.js +1 -1
  74. package/www/3d-editor/js/Sidebar.Geometry.IcosahedronGeometry.js +1 -1
  75. package/www/3d-editor/js/Sidebar.Geometry.LatheGeometry.js +1 -1
  76. package/www/3d-editor/js/Sidebar.Geometry.Modifiers.js +1 -1
  77. package/www/3d-editor/js/Sidebar.Geometry.OctahedronGeometry.js +1 -1
  78. package/www/3d-editor/js/Sidebar.Geometry.PlaneGeometry.js +1 -1
  79. package/www/3d-editor/js/Sidebar.Geometry.RingGeometry.js +1 -1
  80. package/www/3d-editor/js/Sidebar.Geometry.ShapeGeometry.js +1 -1
  81. package/www/3d-editor/js/Sidebar.Geometry.SphereGeometry.js +1 -1
  82. package/www/3d-editor/js/Sidebar.Geometry.TetrahedronGeometry.js +1 -1
  83. package/www/3d-editor/js/Sidebar.Geometry.TextGeometry.js +1 -1
  84. package/www/3d-editor/js/Sidebar.Geometry.TorusGeometry.js +1 -1
  85. package/www/3d-editor/js/Sidebar.Geometry.TorusKnotGeometry.js +1 -1
  86. package/www/3d-editor/js/Sidebar.Geometry.TubeGeometry.js +1 -1
  87. package/www/3d-editor/js/Sidebar.Geometry.js +1 -1
  88. package/www/3d-editor/js/Sidebar.Material.BooleanProperty.js +1 -1
  89. package/www/3d-editor/js/Sidebar.Material.ColorProperty.js +1 -1
  90. package/www/3d-editor/js/Sidebar.Material.ConstantProperty.js +1 -1
  91. package/www/3d-editor/js/Sidebar.Material.MapProperty.js +1 -1
  92. package/www/3d-editor/js/Sidebar.Material.NumberProperty.js +1 -1
  93. package/www/3d-editor/js/Sidebar.Material.Program.js +1 -1
  94. package/www/3d-editor/js/Sidebar.Material.RangeValueProperty.js +1 -1
  95. package/www/3d-editor/js/Sidebar.Material.js +1 -1
  96. package/www/3d-editor/js/Sidebar.Object.js +1 -1
  97. package/www/3d-editor/js/Sidebar.Project.App.js +1 -1
  98. package/www/3d-editor/js/Sidebar.Project.Materials.js +1 -1
  99. package/www/3d-editor/js/Sidebar.Project.Renderer.js +1 -1
  100. package/www/3d-editor/js/Sidebar.Project.Resources.js +1 -1
  101. package/www/3d-editor/js/Sidebar.Project.js +1 -1
  102. package/www/3d-editor/js/Sidebar.Properties.js +1 -1
  103. package/www/3d-editor/js/Sidebar.Scene.js +1 -1
  104. package/www/3d-editor/js/Sidebar.Script.js +1 -1
  105. package/www/3d-editor/js/Sidebar.Settings.History.js +1 -1
  106. package/www/3d-editor/js/Sidebar.Settings.Shortcuts.js +1 -1
  107. package/www/3d-editor/js/Sidebar.Settings.js +1 -1
  108. package/www/3d-editor/js/Sidebar.js +1 -1
  109. package/www/3d-editor/js/Storage.js +1 -1
  110. package/www/3d-editor/js/Strings.js +1 -1
  111. package/www/3d-editor/js/TextureParametersDialog.js +1 -1
  112. package/www/3d-editor/js/Toolbar.js +1 -1
  113. package/www/3d-editor/js/Viewport.Controls.js +1 -1
  114. package/www/3d-editor/js/Viewport.Info.js +1 -1
  115. package/www/3d-editor/js/Viewport.Pathtracer.js +1 -1
  116. package/www/3d-editor/js/Viewport.ViewHelper.js +1 -1
  117. package/www/3d-editor/js/Viewport.XR.js +1 -1
  118. package/www/3d-editor/js/Viewport.js +1 -1
  119. package/www/3d-editor/js/commands/AddObjectCommand.js +1 -1
  120. package/www/3d-editor/js/commands/AddScriptCommand.js +1 -1
  121. package/www/3d-editor/js/commands/Commands.js +1 -1
  122. package/www/3d-editor/js/commands/MoveObjectCommand.js +1 -1
  123. package/www/3d-editor/js/commands/MultiCmdsCommand.js +1 -1
  124. package/www/3d-editor/js/commands/RemoveObjectCommand.js +1 -1
  125. package/www/3d-editor/js/commands/RemoveScriptCommand.js +1 -1
  126. package/www/3d-editor/js/commands/SetColorCommand.js +1 -1
  127. package/www/3d-editor/js/commands/SetGeometryCommand.js +1 -1
  128. package/www/3d-editor/js/commands/SetGeometryValueCommand.js +1 -1
  129. package/www/3d-editor/js/commands/SetMaterialColorCommand.js +1 -1
  130. package/www/3d-editor/js/commands/SetMaterialCommand.js +1 -1
  131. package/www/3d-editor/js/commands/SetMaterialMapCommand.js +1 -1
  132. package/www/3d-editor/js/commands/SetMaterialRangeCommand.js +1 -1
  133. package/www/3d-editor/js/commands/SetMaterialValueCommand.js +1 -1
  134. package/www/3d-editor/js/commands/SetMaterialVectorCommand.js +1 -1
  135. package/www/3d-editor/js/commands/SetPositionCommand.js +1 -1
  136. package/www/3d-editor/js/commands/SetRotationCommand.js +1 -1
  137. package/www/3d-editor/js/commands/SetScaleCommand.js +1 -1
  138. package/www/3d-editor/js/commands/SetSceneCommand.js +1 -1
  139. package/www/3d-editor/js/commands/SetScriptValueCommand.js +1 -1
  140. package/www/3d-editor/js/commands/SetShadowValueCommand.js +1 -1
  141. package/www/3d-editor/js/commands/SetTextureParametersCommand.js +1 -1
  142. package/www/3d-editor/js/commands/SetUuidCommand.js +1 -1
  143. package/www/3d-editor/js/commands/SetValueCommand.js +1 -1
  144. package/www/dist/frontend/bundle/HistoryDirectives-HJXWD2HY.js +1 -0
  145. package/www/dist/frontend/bundle/ReportViewer-6YF7WKZX.js +197 -0
  146. package/www/dist/frontend/bundle/Sha256-BSH7CIPW.js +1 -1
  147. package/www/dist/frontend/bundle/chunk-JAUROOM5.js +21 -0
  148. package/www/dist/frontend/bundle/chunk-KVPT4J7H.js +1 -1
  149. package/www/dist/frontend/bundle/chunk-QLBNVDK5.js +1 -0
  150. package/www/dist/frontend/bundle/common/IobrokerHandler.js +1 -1
  151. package/www/dist/frontend/bundle/runtime/CustomControls.js +1 -1
  152. package/www/dist/frontend/bundle/runtime/ScreenViewer.js +1 -1
  153. package/www/dist/frontend/bundle/runtime/controls.js +3 -3
  154. package/www/dist/frontend/bundle/runtime/init.js +3 -3
  155. package/www/dist/frontend/common/BindingExpression.js +1 -1
  156. package/www/dist/frontend/common/BuiltinTranslations.js +1 -1
  157. package/www/dist/frontend/common/Common.globals.js +1 -1
  158. package/www/dist/frontend/common/ConfirmWrite.js +1 -1
  159. package/www/dist/frontend/common/EChartsOption.js +1 -1
  160. package/www/dist/frontend/common/HistoryDirective.js +1 -1
  161. package/www/dist/frontend/common/HistoryOptions.js +1 -1
  162. package/www/dist/frontend/common/HistoryQuery.js +1 -1
  163. package/www/dist/frontend/common/HistoryQueryViews.js +1 -1
  164. package/www/dist/frontend/common/IobrokerHandler.js +1 -1
  165. package/www/dist/frontend/common/PlantPaxResolve.js +1 -1
  166. package/www/dist/frontend/common/ReadQuality.js +1 -1
  167. package/www/dist/frontend/common/ReportAllowlist.js +1 -1
  168. package/www/dist/frontend/common/ReportSchedule.js +1 -1
  169. package/www/dist/frontend/common/ReportTheme.js +1 -1
  170. package/www/dist/frontend/common/Runtime.js +1 -1
  171. package/www/dist/frontend/common/Sha256.js +1 -1
  172. package/www/dist/frontend/common/StartScreen.js +1 -0
  173. package/www/dist/frontend/common/StateDiagnostics.js +1 -1
  174. package/www/dist/frontend/common/StateQuality.js +1 -1
  175. package/www/dist/frontend/common/WriteFeedback.js +1 -1
  176. package/www/dist/frontend/config/CommandHandling.js +1 -1
  177. package/www/dist/frontend/config/ConfigureWebcomponentDesigner.js +1 -1
  178. package/www/dist/frontend/config/ConfirmWindow.js +1 -1
  179. package/www/dist/frontend/config/DockHelper.js +1 -1
  180. package/www/dist/frontend/config/HistoryQueryWindow.js +1 -1
  181. package/www/dist/frontend/config/IobrokerWebui3DDriveEngine.js +1 -1
  182. package/www/dist/frontend/config/IobrokerWebui3DScreenEditor.js +2 -2
  183. package/www/dist/frontend/config/IobrokerWebui3DScreenPropertiesPanel.js +3 -3
  184. package/www/dist/frontend/config/IobrokerWebui3DScreenViewer.js +3 -3
  185. package/www/dist/frontend/config/IobrokerWebuiAppShell.js +3 -3
  186. package/www/dist/frontend/config/IobrokerWebuiBindingsEditor.js +1 -1
  187. package/www/dist/frontend/config/IobrokerWebuiConfirmationWrapper.js +3 -3
  188. package/www/dist/frontend/config/IobrokerWebuiControlPropertiesEditor.js +3 -3
  189. package/www/dist/frontend/config/IobrokerWebuiDynamicPropsEditor.js +1 -1
  190. package/www/dist/frontend/config/IobrokerWebuiEventAssignment.js +1 -1
  191. package/www/dist/frontend/config/IobrokerWebuiIconsView.js +3 -3
  192. package/www/dist/frontend/config/IobrokerWebuiMonacoEditor.js +3 -3
  193. package/www/dist/frontend/config/IobrokerWebuiPlantPaxTypesEditor.js +1 -1
  194. package/www/dist/frontend/config/IobrokerWebuiPropertyGrid.js +1 -1
  195. package/www/dist/frontend/config/IobrokerWebuiScreenEditor.js +1 -1
  196. package/www/dist/frontend/config/IobrokerWebuiScreensView.js +3 -3
  197. package/www/dist/frontend/config/IobrokerWebuiSecurityEditor.js +1 -1
  198. package/www/dist/frontend/config/IobrokerWebuiSignalPropertyEditor.js +1 -1
  199. package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +3 -3
  200. package/www/dist/frontend/config/IobrokerWebuiTranslationEditor.js +1 -1
  201. package/www/dist/frontend/config/IobrokerWebuiWidgetGallery.js +2 -2
  202. package/www/dist/frontend/config/RightDockStrip.js +1 -1
  203. package/www/dist/frontend/config/UprightOverlays.js +1 -1
  204. package/www/dist/frontend/helper/DialogHelper.js +2 -2
  205. package/www/dist/frontend/helper/EditorOfDesignItem.js +1 -1
  206. package/www/dist/frontend/helper/EsprimaHelper.js +1 -1
  207. package/www/dist/frontend/helper/Helper.js +1 -1
  208. package/www/dist/frontend/helper/LayoutHelper.js +1 -1
  209. package/www/dist/frontend/helper/XmlHelper.js +1 -1
  210. package/www/dist/frontend/helper/ZipHelper.js +1 -1
  211. package/www/dist/frontend/runtime/AnimationService.js +1 -1
  212. package/www/dist/frontend/runtime/CustomControls.js +1 -1
  213. package/www/dist/frontend/runtime/DynamicElementProperties.js +1 -1
  214. package/www/dist/frontend/runtime/EChart.js +1 -1
  215. package/www/dist/frontend/runtime/HabPanelLikeMenu.js +3 -3
  216. package/www/dist/frontend/runtime/HistoryDirectives.js +1 -1
  217. package/www/dist/frontend/runtime/NavigationShell.js +1 -1
  218. package/www/dist/frontend/runtime/ReportViewer.js +3 -3
  219. package/www/dist/frontend/runtime/RuntimeTheme.js +1 -1
  220. package/www/dist/frontend/runtime/ScreenViewer.js +3 -3
  221. package/www/dist/frontend/runtime/TranslateableText.js +2 -2
  222. package/www/dist/frontend/runtime/VisibilityService.js +1 -1
  223. package/www/dist/frontend/runtime/controls.js +1 -1
  224. package/www/dist/frontend/runtime/init.js +1 -1
  225. package/www/dist/frontend/scripting/IobrokerWebuiScriptSystem.js +1 -1
  226. package/www/dist/frontend/scripting/blockly/OpenScreen.js +1 -1
  227. package/www/dist/frontend/scripting/blockly/webuiBlocklyToolbox.js +1 -1
  228. package/www/dist/frontend/services/DynamicPropertiesHelper.js +1 -1
  229. package/www/dist/frontend/services/IobrokerWebuiBindableLocalObjectsService.js +1 -1
  230. package/www/dist/frontend/services/IobrokerWebuiBindableObjectDragDropService.js +1 -1
  231. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsForPropertiesService.js +1 -1
  232. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsService.js +1 -1
  233. package/www/dist/frontend/services/IobrokerWebuiConfigButtonProvider.js +1 -1
  234. package/www/dist/frontend/services/IobrokerWebuiCopyPasteService.js +1 -1
  235. package/www/dist/frontend/services/IobrokerWebuiCustomControlEventsService.js +1 -1
  236. package/www/dist/frontend/services/IobrokerWebuiCustomElementContextMenu.js +1 -1
  237. package/www/dist/frontend/services/IobrokerWebuiDemoProviderService.js +1 -1
  238. package/www/dist/frontend/services/IobrokerWebuiDynamicPropertiesService.js +1 -1
  239. package/www/dist/frontend/services/IobrokerWebuiEChartPropertiesService.js +1 -1
  240. package/www/dist/frontend/services/IobrokerWebuiEventsService.js +1 -1
  241. package/www/dist/frontend/services/IobrokerWebuiExternalDragDropService.js +1 -1
  242. package/www/dist/frontend/services/IobrokerWebuiLitPropertiesService.js +1 -1
  243. package/www/dist/frontend/services/IobrokerWebuiModelCommandService.js +1 -1
  244. package/www/dist/frontend/services/IobrokerWebuiPropertiesService.js +1 -1
  245. package/www/dist/frontend/services/IobrokerWebuiPropertyGroupsService.js +1 -1
  246. package/www/dist/frontend/services/IobrokerWebuiRefactorService.js +1 -1
  247. package/www/dist/frontend/services/IobrokerWebuiReportContextMenu.js +1 -1
  248. package/www/dist/frontend/services/IobrokerWebuiScreenContextMenu.js +1 -1
  249. package/www/dist/frontend/services/IobrokerWebuiSpecialPropertiesService.js +1 -1
  250. package/www/dist/frontend/services/IobrokerWebuiSpectrumEditorService.js +1 -1
  251. package/www/dist/frontend/services/IobrokerWebuiVisibilityPropertiesService.js +1 -1
  252. package/www/dist/frontend/services/LayoutComponentsPropertiesService.js +1 -1
  253. package/www/dist/frontend/widgets/OrthoFlowLineTool.js +1 -1
  254. package/www/dist/frontend/widgets/ReportToolbox.js +3 -3
  255. package/www/dist/frontend/widgets/customElementsObserver.js +1 -1
  256. package/www/dist/frontend/widgets/importWidgetFiles.js +1 -1
  257. package/www/dist/frontend/widgets/importWidgetFilesRuntime.js +1 -1
  258. package/www/dist/frontend/widgets/layout-components.js +1 -1
  259. package/www/dist/frontend/widgets/layout-placement-services.js +1 -1
  260. package/www/dist/frontend/widgets/testElement.js +1 -1
  261. package/www/runtime.html +28 -13
  262. package/www/dist/frontend/bundle/HistoryDirectives-2D3MY36Q.js +0 -1
  263. package/www/dist/frontend/bundle/ReportViewer-VIW3KLIA.js +0 -197
  264. package/www/dist/frontend/bundle/chunk-IFBVYILO.js +0 -1
  265. package/www/dist/frontend/bundle/chunk-YBPYQWW6.js +0 -21
package/README.md CHANGED
@@ -302,6 +302,30 @@ runtime.html?screenName=screen2
302
302
  Placeholder for next versions:
303
303
  ### __WORK IN PROGRESS__
304
304
  -->
305
+ ### 1.178.0 (2026-10-06)
306
+ * (screens) startup screen: right-click a screen → Set as startup screen (everyone) or for a user group; Settings → Startup screen (default + ordered group rules); rename/delete keep the rules in step; no startup screen and no "start" → a message instead of an error, and a link to a missing screen says so
307
+
308
+ ### 1.177.1 (2026-10-06)
309
+ * (navigation) controls in the app bar (user badge, logout, theme toggle/selector …) take the bar's colours instead of the page theme's, so they stay readable on a dark bar in light themes; app bar theme "auto" still follows the page
310
+
311
+ ### 1.177.0 (2026-10-06)
312
+ * (navigation) a sidebar runs the full height with the app bar beside it; the ☰ goes with the menu (its top corner, in the menu's colours) and cycles full → icons → hidden again; hidden, it docks in the app bar's corner; top and bottom bars keep it in their own place
313
+
314
+ ### 1.176.0 (2026-10-06)
315
+ * (gokturk413) feat(navigation): the sidebar has its own toggle in its header (≡‹ folds to icons, ≡› opens), as in most UIs; the app bar's ☰ only brings back a hidden menu; on a phone the toggle hides the menu
316
+
317
+ ### 1.175.2 (2026-10-06)
318
+ * (gokturk413) feat(navigation): app bar on a phone — the centre and right slots open from a ⋮ button as a panel; the caption shortens
319
+
320
+ ### 1.175.1 (2026-10-06)
321
+ * (gokturk413) feat(controls): dashboard panel-card is a container (controls inside flow and wrap), setpoint-slider keeps room for its value bubble; side menu starts closed on phones; docs: building a dashboard that works on a phone
322
+
323
+ ### 1.175.0 (2026-10-06)
324
+ * (gokturk413) fix: a session that ran out no longer reloads the page forever ("?login&href=" loop): the session is renewed with the refresh token, else web.0's login page brings the user back to the same screen; Global → Designer holds the built-in element sets (mywebui, layout, native), Controls → Designer only the project's npm packages; screen viewer with objectType report lists the reports at once; report Generate → Signal works with local_ values and report properties (watched by the page that shows the report)
325
+
326
+ ### 1.174.0 (2026-10-02)
327
+ * (gokturk413) feat(controls): dashboard controls in global/controls/dashboard — panel-card, kpi-tile, countdown-tile, conveyor, conveyor-box, command-group, setpoint-slider (with range), logic-gate, status-chip, boiler, dial-gauge, time-spinner, notification-bell; demo screen dashboard/demo; Animations window: Start offset (0–1) so several elements on one path run evenly spaced
328
+
305
329
  ### 1.173.1 (2026-10-02)
306
330
  * (gokturk413) fix(reports): a report asked through the gateway is rendered for the gateway's user; every browser-facing relay (rest-api, webapi …) needs a proven user
307
331
 
package/admin/i18n.json CHANGED
@@ -182,5 +182,47 @@
182
182
  "tr": "1. Adaptörü başlatın: 14 günlük deneme otomatik başlar\n2. Yukarıdaki Hardware ID satırının tamamını kopyalayın\n3. Lisans anahtarı almak için gokturk413'e gönderin\n4. Anahtarı buraya girip kaydedin (adaptör yeniden başlar)\n\n💡 Deneme süresi anahtarsız biterse runtime durur. Deneme sırasında yapılan proje, anahtar girildikten sonra çalışmaya devam eder.\n💡 Yeni bir bilgisayar için onun Hardware ID'sini gönderip ayrı bir anahtar alın.",
183
183
  "ru": "1. Запустите адаптер: 14-дневный пробный период начнётся автоматически\n2. Скопируйте всю строку Hardware ID выше\n3. Отправьте её gokturk413, чтобы получить лицензионный ключ\n4. Введите ключ здесь и сохраните (адаптер перезапустится)\n\n💡 Если пробный период закончится без ключа, runtime останавливается. Проект, созданный в пробный период, продолжит работать после ввода ключа.\n💡 Для нового компьютера отправьте его Hardware ID и получите для него ключ.",
184
184
  "de": "1. Adapter starten: eine 14-tägige Testphase beginnt automatisch\n2. Die ganze Hardware-ID-Zeile oben kopieren\n3. An gokturk413 senden, um einen Lizenzschlüssel zu erhalten\n4. Den Schlüssel hier eingeben und speichern (der Adapter startet neu)\n\n💡 Endet die Testphase ohne Schlüssel, stoppt die Runtime. Ein in der Testphase erstelltes Projekt läuft nach Eingabe des Schlüssels weiter.\n💡 Für einen neuen Computer dessen Hardware-ID senden und einen eigenen Schlüssel erhalten."
185
+ },
186
+ "Offline activation": {
187
+ "en": "Offline activation",
188
+ "az": "Oflayn aktivləşdirmə",
189
+ "tr": "Çevrimdışı etkinleştirme",
190
+ "ru": "Офлайн-активация",
191
+ "de": "Offline-Aktivierung"
192
+ },
193
+ "No internet on site? Create an activation request, send it to gokturk413 (email, USB), and paste the key or the whole response file you get back into the License Key field above.": {
194
+ "en": "No internet on site? Create an activation request, send it to gokturk413 (email, USB), and paste the key or the whole response file you get back into the License Key field above.",
195
+ "az": "Obyektdə internet yoxdur? Aktivləşdirmə sorğusu yaradın, gokturk413-ə göndərin (e-poçt, USB) və geri gələn açarı və ya cavab faylının hamısını yuxarıdakı License Key sahəsinə yapışdırın.",
196
+ "tr": "Sahada internet yok mu? Bir etkinleştirme isteği oluşturun, gokturk413'e gönderin (e-posta, USB) ve gelen anahtarı veya yanıt dosyasının tamamını yukarıdaki License Key alanına yapıştırın.",
197
+ "ru": "Нет интернета на объекте? Создайте запрос активации, отправьте его gokturk413 (почта, USB) и вставьте полученный ключ или весь файл ответа в поле License Key выше.",
198
+ "de": "Kein Internet vor Ort? Aktivierungsanfrage erstellen, an gokturk413 senden (E-Mail, USB) und den erhaltenen Schlüssel oder die ganze Antwortdatei oben in das Feld License Key einfügen."
199
+ },
200
+ "Create activation request": {
201
+ "en": "Create activation request",
202
+ "az": "Aktivləşdirmə sorğusu yarat",
203
+ "tr": "Etkinleştirme isteği oluştur",
204
+ "ru": "Создать запрос активации",
205
+ "de": "Aktivierungsanfrage erstellen"
206
+ },
207
+ "Activation request": {
208
+ "en": "Activation request",
209
+ "az": "Aktivləşdirmə sorğusu",
210
+ "tr": "Etkinleştirme isteği",
211
+ "ru": "Запрос активации",
212
+ "de": "Aktivierungsanfrage"
213
+ },
214
+ "Copy this line (copy button) and send it. It holds the Hardware IDs of this computer, the environment (physical, VM or container) and the host name; it contains no secret.": {
215
+ "en": "Copy this line (copy button) and send it. It holds the Hardware IDs of this computer, the environment (physical, VM or container) and the host name; it contains no secret.",
216
+ "az": "Bu sətri kopyalayın (kopyalama düyməsi) və göndərin. İçində bu kompüterin Hardware ID-ləri, mühit (fiziki, VM və ya konteyner) və host adı var; gizli məlumat yoxdur.",
217
+ "tr": "Bu satırı kopyalayın (kopyala düğmesi) ve gönderin. Bu bilgisayarın Hardware ID'lerini, ortamı (fiziksel, VM veya konteyner) ve ana bilgisayar adını içerir; gizli bilgi içermez.",
218
+ "ru": "Скопируйте эту строку (кнопка копирования) и отправьте. В ней Hardware ID этого компьютера, среда (физическая, ВМ или контейнер) и имя хоста; секретов нет.",
219
+ "de": "Diese Zeile kopieren (Kopier-Schaltfläche) und senden. Sie enthält die Hardware-IDs dieses Computers, die Umgebung (physisch, VM oder Container) und den Hostnamen; keine Geheimnisse."
220
+ },
221
+ "Paste the licence key, or the whole activation response file. Contact gokturk413 for a key.": {
222
+ "en": "Paste the licence key, or the whole activation response file. Contact gokturk413 for a key.",
223
+ "az": "Lisenziya açarını və ya aktivləşdirmə cavab faylının hamısını yapışdırın. Açar üçün gokturk413 ilə əlaqə saxlayın.",
224
+ "tr": "Lisans anahtarını veya etkinleştirme yanıt dosyasının tamamını yapıştırın. Anahtar için gokturk413 ile iletişime geçin.",
225
+ "ru": "Вставьте лицензионный ключ или весь файл ответа активации. За ключом обращайтесь к gokturk413.",
226
+ "de": "Lizenzschlüssel oder die ganze Aktivierungs-Antwortdatei einfügen. Schlüssel bei gokturk413 anfragen."
185
227
  }
186
228
  }
@@ -26,7 +26,7 @@
26
26
  "sm": 12,
27
27
  "md": 8,
28
28
  "lg": 6,
29
- "help": "Enter your license key here. Contact gokturk413 for a valid license key.",
29
+ "help": "Paste the licence key, or the whole activation response file. Contact gokturk413 for a key.",
30
30
  "visible": true,
31
31
  "repeat": false
32
32
  },
@@ -82,6 +82,40 @@
82
82
  "borderRadius": "4px"
83
83
  }
84
84
  },
85
+ "_activationHeader": {
86
+ "type": "header",
87
+ "text": "Offline activation",
88
+ "size": 4
89
+ },
90
+ "_activationInfo": {
91
+ "type": "staticText",
92
+ "text": "No internet on site? Create an activation request, send it to gokturk413 (email, USB), and paste the key or the whole response file you get back into the License Key field above.",
93
+ "style": {
94
+ "fontSize": 13,
95
+ "marginBottom": 8
96
+ }
97
+ },
98
+ "activationRequest": {
99
+ "type": "text",
100
+ "label": "Activation request",
101
+ "readOnly": true,
102
+ "copyToClipboard": true,
103
+ "sm": 12,
104
+ "md": 8,
105
+ "lg": 8,
106
+ "help": "Copy this line (copy button) and send it. It holds the Hardware IDs of this computer, the environment (physical, VM or container) and the host name; it contains no secret."
107
+ },
108
+ "_getActivationBtn": {
109
+ "type": "sendTo",
110
+ "label": "Create activation request",
111
+ "command": "getActivationRequest",
112
+ "jsonData": "{}",
113
+ "variant": "contained",
114
+ "icon": "send",
115
+ "useNative": true,
116
+ "sm": 12,
117
+ "md": 4
118
+ },
85
119
  "_separator": {
86
120
  "type": "divider"
87
121
  },
@@ -0,0 +1,51 @@
1
+ {
2
+ "properties": {
3
+ "level": {
4
+ "type": "number",
5
+ "default": 55
6
+ },
7
+ "pv": {
8
+ "type": "number",
9
+ "default": 74
10
+ },
11
+ "sp": {
12
+ "type": "number",
13
+ "default": 75
14
+ },
15
+ "heating": {
16
+ "type": "boolean",
17
+ "default": "true"
18
+ },
19
+ "min": {
20
+ "type": "number",
21
+ "default": 0
22
+ },
23
+ "max": {
24
+ "type": "number",
25
+ "default": 100
26
+ },
27
+ "unit": {
28
+ "type": "string",
29
+ "default": "°C"
30
+ },
31
+ "decimals": {
32
+ "type": "number",
33
+ "default": 0
34
+ },
35
+ "liquidColor": {
36
+ "type": "color",
37
+ "default": "#e8453c"
38
+ },
39
+ "showValues": {
40
+ "type": "boolean",
41
+ "default": "true"
42
+ }
43
+ },
44
+ "settings": {
45
+ "width": "330px",
46
+ "height": "260px"
47
+ },
48
+ "html": "<div id=\"b\" class=\"boil\"><svg class=\"vessel\" viewBox=\"0 0 170 250\" aria-hidden=\"true\"><defs><clipPath id=\"vclip\"><rect x=\"35\" y=\"40\" width=\"100\" height=\"160\" rx=\"22\"/></clipPath><linearGradient id=\"steel\" x1=\"0\" x2=\"1\"><stop offset=\"0\" stop-color=\"#5c6573\"/><stop offset=\".45\" stop-color=\"#b9c2ce\"/><stop offset=\"1\" stop-color=\"#4a525e\"/></linearGradient><linearGradient id=\"liq\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\"><stop id=\"lq1\" offset=\"0\" stop-color=\"#ff6b5e\"/><stop id=\"lq2\" offset=\"1\" stop-color=\"#b3221b\"/></linearGradient></defs><rect x=\"70\" y=\"14\" width=\"30\" height=\"20\" rx=\"4\" style=\"fill:url(#steel)\"/><rect x=\"32\" y=\"32\" width=\"106\" height=\"176\" rx=\"26\" style=\"fill:url(#steel)\"/><g clip-path=\"url(#vclip)\"><rect x=\"35\" y=\"40\" width=\"100\" height=\"160\" style=\"fill:#1d2128\"/><g id=\"liquid\" transform=\"translate(0,120)\"><path class=\"wave\" d=\"M0 10 Q 25 0 50 10 T 100 10 T 150 10 T 200 10 V 220 H 0 Z\" style=\"fill:url(#liq)\"/></g><rect x=\"44\" y=\"40\" width=\"10\" height=\"160\" style=\"fill:rgba(255,255,255,.12)\"/></g><rect x=\"112\" y=\"60\" width=\"7\" height=\"120\" rx=\"3.5\" style=\"fill:#e8ecf1\"/><rect id=\"thermo\" x=\"113.5\" y=\"100\" width=\"4\" height=\"78\" rx=\"2\" style=\"fill:#ff4d4d\"/><circle cx=\"115.5\" cy=\"184\" r=\"7\" style=\"fill:#ff4d4d;stroke:#e8ecf1;stroke-width:2\"/><circle cx=\"118\" cy=\"56\" r=\"13\" style=\"fill:#e8ecf1;stroke:#7d8693;stroke-width:2\"/><line id=\"needle\" x1=\"118\" y1=\"56\" x2=\"125\" y2=\"50\" style=\"stroke:#222;stroke-width:2\"/><circle cx=\"85\" cy=\"176\" r=\"13\" style=\"fill:#e8ecf1;stroke:#7d8693;stroke-width:2\"/><line x1=\"85\" y1=\"176\" x2=\"80\" y2=\"168\" style=\"stroke:#222;stroke-width:2\"/><rect x=\"40\" y=\"206\" width=\"90\" height=\"34\" rx=\"4\" style=\"fill:#2b3038\"/><g id=\"flame\" class=\"flame\"><path d=\"M70 238 C 62 226 72 220 74 212 C 78 222 84 214 85 204 C 90 216 100 220 97 230 C 104 226 104 236 100 238 Z\" style=\"fill:#ff9a1f\"/><path d=\"M78 238 C 74 230 80 226 82 220 C 86 228 92 228 92 238 Z\" style=\"fill:#ffe066\"/></g></svg><div id=\"vals\" class=\"vals\"><span class=\"pv mono\"><span id=\"pv\"></span><small id=\"u1\"></small></span><span class=\"sp mono\"><span id=\"sp\"></span><small id=\"u2\"></small></span></div></div>",
49
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.boil{height:100%;display:flex;align-items:center;gap:12px}\n.vessel{height:100%;max-height:100%;aspect-ratio:170/250;overflow:visible;flex:none}\n.wave{animation:wave 3s ease-in-out infinite}@keyframes wave{50%{transform:translateX(-14px)}}\n.flame{transform-origin:85px 238px;animation:flick .25s ease-in-out infinite alternate}@keyframes flick{to{transform:scaleY(.82) scaleX(1.06)}}\n.vals{display:flex;flex-direction:column;gap:2px}\n.pv{font-size:36px;font-weight:600}.sp{font-size:26px;font-weight:600;color:var(--db-glow)}\n.vals small{font-size:.6em;margin-left:2px}\n.boil.novals .vals{display:none}\n",
50
+ "script": "export function levelY(level){ const l = Math.max(0, Math.min(100, Number(level) || 0)); return 40 + (1 - l / 100) * 150; }\nexport function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const yes = (v) => v === true || v === 'true' || v === 1;\n const upd = () => {\n const min = Number(instance.min) || 0, max = Number(instance.max) || 100, dec = Math.max(0, Number(instance.decimals) || 0);\n $('liquid').setAttribute('transform', 'translate(0,' + levelY(instance.level).toFixed(1) + ')');\n const pv = Number(instance.pv), f = Math.max(0, Math.min(1, ((isFinite(pv) ? pv : min) - min) / ((max - min) || 1)));\n $('thermo').setAttribute('y', String(180 - f * 110)); $('thermo').setAttribute('height', String(Math.max(2, f * 110)));\n const ang = (-140 + f * 280) * Math.PI / 180;\n $('needle').setAttribute('x2', String(118 + 9 * Math.sin(ang))); $('needle').setAttribute('y2', String(56 - 9 * Math.cos(ang)));\n $('flame').style.display = yes(instance.heating) ? '' : 'none';\n const c = instance.liquidColor || '#e8453c';\n $('lq1').setAttribute('stop-color', c); $('lq2').setAttribute('stop-color', c); $('lq2').setAttribute('stop-opacity', '.75');\n $('pv').textContent = isFinite(pv) ? pv.toFixed(dec) : '—';\n const sp = Number(instance.sp); $('sp').textContent = isFinite(sp) ? sp.toFixed(dec) : '—';\n $('u1').textContent = $('u2').textContent = instance.unit || '';\n $('b').classList.toggle('novals', instance.showValues === false || instance.showValues === 'false');\n };\n for(const p of ['level', 'pv', 'sp', 'heating', 'min', 'max', 'unit', 'decimals', 'liquid-color', 'show-values']) instance._assignEvent(p + '-changed', upd);\n upd();\n}\n"
51
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "properties": {
3
+ "signal": {
4
+ "type": "signal",
5
+ "default": ""
6
+ },
7
+ "buttons": {
8
+ "type": "string",
9
+ "default": "[{\"label\": \"ON\", \"value\": true, \"tone\": \"ok\"}, {\"label\": \"OFF\", \"value\": false, \"tone\": \"off\"}, {\"label\": \"RESET\", \"value\": true, \"tone\": \"warn\", \"momentary\": true, \"signal\": \"\"}]"
10
+ }
11
+ },
12
+ "settings": {
13
+ "width": "300px",
14
+ "height": "44px"
15
+ },
16
+ "html": "<div id=\"g\" class=\"cmdg\" role=\"group\"></div>",
17
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.cmdg{height:100%;display:inline-flex;border-radius:10px;overflow:hidden;border:1px solid var(--ui-border,#4a5262)}\nbutton{font:600 15px 'IBM Plex Sans','Segoe UI',sans-serif;letter-spacing:.04em;padding:0 22px;border:0;border-right:1px solid var(--ui-border,#4a5262);background:var(--ui-surface-2,#3a414d);color:var(--ui-dim,#9aa5b5);cursor:pointer}\nbutton:last-child{border-right:0}\nbutton.cur{background:linear-gradient(180deg,color-mix(in srgb,var(--tone) 100%,#fff 10%),color-mix(in srgb,var(--tone) 80%,#000));color:#fff}\nbutton.mom{color:var(--tone)}\nbutton.mom.flash,button.mom:active{background:linear-gradient(180deg,color-mix(in srgb,var(--tone) 100%,#fff 15%),color-mix(in srgb,var(--tone) 80%,#000));color:#1d1404}\nbutton:focus-visible{outline:2px solid var(--accent,#57b6ff);outline-offset:-3px}\n",
18
+ "script": "function dbSubscribe(instance, key, id, onVal){\n dbUnsub(instance, key);\n if(!id || !window.IOB) return;\n instance.__dbSubs = instance.__dbSubs || {};\n const cb = function(_i, s){ onVal(s ? s.val : null); };\n instance.__dbSubs[key] = [id, cb];\n try{ window.IOB.subscribeState(id, cb); }catch(e){}\n try{ window.IOB.getState(id).then(function(s){ onVal(s ? s.val : null); }).catch(function(){}); }catch(e){}\n // read quality knows this state (badge on the control when the value cannot be trusted)\n try{ instance.__dbRq = instance.__dbRq || {}; instance.__dbRq[key] = window.IOB.watchReadQuality && window.IOB.watchReadQuality(instance, id, { prop: key, twoWay: true }); }catch(e){}\n}\nfunction dbUnsub(instance, key){\n const p = instance.__dbSubs && instance.__dbSubs[key];\n if(p){ try{ window.IOB.unsubscribeState(p[0], p[1]); }catch(e){} delete instance.__dbSubs[key]; }\n const r = instance.__dbRq && instance.__dbRq[key];\n if(typeof r === 'function'){ try{ r(); }catch(e){} }\n if(instance.__dbRq) delete instance.__dbRq[key];\n}\nfunction dbUnsubAll(instance){ for(const k of Object.keys(instance.__dbSubs || {})) dbUnsub(instance, k); }\n// a write through IOB.setState: Confirm & Protect may ask first; cancelled = nothing written\nfunction dbWrite(instance, id, val, onCancel){\n if(!id || !window.IOB) return Promise.resolve(false);\n return Promise.resolve(window.IOB.setState(id, val, false, { feedback: instance })).then(function(){ return true; }, function(e){\n if(e && e.cancelled){ if(onCancel) onCancel(); }\n else console.warn('[dashboard] write failed', id, e);\n return false;\n });\n}\nfunction dbBool(v){ return v === true || v === 1 || v === 'true' || v === '1' || v === 'on'; }\nfunction dbNum(v, d){ const n = Number(v); return isFinite(n) ? n : d; }\nexport function parseButtons(text){\n try{ const a = JSON.parse(text || '[]'); return Array.isArray(a) ? a.filter(b => b && b.label != null) : []; }catch(e){ return []; }\n}\nexport function sameValue(a, b){\n if(a === b) return true;\n if(a == null || b == null) return false;\n if(typeof a === 'boolean' || typeof b === 'boolean') return dbBool(a) === dbBool(b);\n return String(a) === String(b);\n}\nexport function init(instance){\n const g = instance._getDomElement('g');\n let current;\n const paint = () => {\n for(const b of g.children){ const d = b.__def; b.classList.toggle('cur', !d.momentary && sameValue(current, d.value)); b.setAttribute('aria-pressed', String(b.classList.contains('cur'))); }\n };\n const build = () => {\n g.textContent = '';\n for(const d of parseButtons(instance.buttons)){\n const b = document.createElement('button');\n b.type = 'button'; b.textContent = d.label; b.__def = d;\n b.className = 't-' + (d.tone || 'accent') + (d.momentary ? ' mom' : '');\n b.addEventListener('click', async () => {\n const id = d.signal || instance.signal;\n if(d.momentary){\n b.classList.add('flash'); setTimeout(() => b.classList.remove('flash'), 400);\n // a pulse: the value, then back (true → false, 1 → 0)\n if(await dbWrite(instance, id, d.value)) setTimeout(() => dbWrite(instance, id, typeof d.value === 'boolean' ? !d.value : 0), 500);\n } else dbWrite(instance, id, d.value);\n });\n g.appendChild(b);\n }\n paint();\n };\n const sub = () => dbSubscribe(instance, 'signal', instance.signal, (v) => { current = v; paint(); });\n instance._assignEvent('buttons-changed', build);\n instance._assignEvent('signal-changed', sub);\n build(); sub();\n}\nexport function disconnectedCallback(instance){ dbUnsubAll(instance); }\n"
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "properties": {
3
+ "color": {
4
+ "type": "color",
5
+ "default": "#d9b37c"
6
+ },
7
+ "label": {
8
+ "type": "string",
9
+ "default": ""
10
+ }
11
+ },
12
+ "settings": {
13
+ "width": "58px",
14
+ "height": "40px"
15
+ },
16
+ "html": "<div id=\"b\" class=\"box\"><span id=\"lbl\"></span></div>",
17
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.box{height:100%;border-radius:5px;position:relative;display:grid;place-items:center;font:600 10px 'IBM Plex Mono',monospace;color:rgba(0,0,0,.55);\n background:linear-gradient(180deg,color-mix(in srgb,var(--c) 100%,#fff 12%),color-mix(in srgb,var(--c) 85%,#000));box-shadow:inset 0 0 0 1px rgba(0,0,0,.18),0 4px 8px rgba(0,0,0,.25)}\n.box::after{content:\"\";position:absolute;left:46%;top:0;bottom:0;width:8%;background:rgba(0,0,0,.1)}\n",
18
+ "script": "export function init(instance){\n const upd = () => { const b = instance._getDomElement('b'); b.style.setProperty('--c', instance.color || '#d9b37c'); instance._getDomElement('lbl').textContent = instance.label || ''; };\n instance._assignEvent('color-changed', upd); instance._assignEvent('label-changed', upd);\n upd();\n}\n"
19
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "properties": {
3
+ "running": {
4
+ "type": "boolean",
5
+ "default": "true"
6
+ },
7
+ "speed": {
8
+ "type": "number",
9
+ "default": 50
10
+ },
11
+ "beacon": {
12
+ "type": "boolean",
13
+ "default": "true"
14
+ },
15
+ "beaconColor": {
16
+ "type": "color",
17
+ "default": "#ffc93a"
18
+ },
19
+ "direction": {
20
+ "type": "enum",
21
+ "values": [
22
+ "right",
23
+ "left"
24
+ ],
25
+ "default": "right"
26
+ }
27
+ },
28
+ "settings": {
29
+ "width": "560px",
30
+ "height": "80px"
31
+ },
32
+ "html": "<div id=\"c\" class=\"conv\"><div class=\"motor\"></div><div class=\"belt\"><div id=\"surf\" class=\"surf\"></div></div><span class=\"roll\" style=\"left:34px\"></span><span class=\"roll\" style=\"left:24%\"></span><span class=\"roll\" style=\"left:44%\"></span><span class=\"roll\" style=\"left:64%\"></span><span class=\"roll\" style=\"left:84%\"></span><span id=\"beacon\" class=\"beacon\"></span></div>",
33
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.conv{position:relative;height:100%;min-height:60px}\n.motor{position:absolute;left:0;bottom:10%;width:30px;height:62%;border-radius:6px;background:linear-gradient(90deg,#3a404a,#5e6672,#3a404a)}\n.belt{position:absolute;left:18px;right:40px;bottom:12%;height:44%;border-radius:999px;background:linear-gradient(180deg,#4b5361,#2a2f38);box-shadow:inset 0 2px 0 rgba(255,255,255,.08),0 6px 14px rgba(0,0,0,.35);overflow:hidden}\n.surf{position:absolute;inset:18% 18px;border-radius:10px;background:repeating-linear-gradient(90deg,#20252d 0 10px,#262c35 10px 20px);animation:belt var(--dur,1.6s) linear infinite;animation-play-state:var(--run,running)}\n.conv.left .surf{animation-direction:reverse}\n@keyframes belt{to{background-position:20px 0}}\n.roll{position:absolute;bottom:calc(12% + 7px);width:18px;height:18px;border-radius:50%;background:radial-gradient(circle at 40% 35%,#c9d1dc,#6d7684 60%,#3d434d);border:1px solid #2a2f37}\n.beacon{position:absolute;right:16px;bottom:58%;width:18px;height:26px;border-radius:9px 9px 3px 3px;background:#7a5a10}\n.beacon.on{background:var(--bc);box-shadow:0 0 14px var(--bc),0 0 34px color-mix(in srgb,var(--bc) 50%,transparent);animation:blink 1s steps(2) infinite}\n@keyframes blink{50%{opacity:.45}}\n",
34
+ "script": "export function beltSeconds(speed){ const s = Math.max(1, Math.min(100, Number(speed) || 0)); return 1.6 * 50 / s; }\nexport function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const yes = (v) => v === true || v === 'true';\n const upd = () => {\n const c = $('c');\n const run = yes(instance.running) && Number(instance.speed) > 0;\n c.style.setProperty('--run', run ? 'running' : 'paused');\n c.style.setProperty('--dur', beltSeconds(instance.speed).toFixed(2) + 's');\n c.classList.toggle('left', instance.direction === 'left');\n const b = $('beacon');\n b.style.setProperty('--bc', instance.beaconColor || '#ffc93a');\n b.classList.toggle('on', yes(instance.beacon));\n };\n for(const p of ['running', 'speed', 'beacon', 'beacon-color', 'direction']) instance._assignEvent(p + '-changed', upd);\n upd();\n}\n"
35
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "properties": {
3
+ "label": {
4
+ "type": "string",
5
+ "default": "Remaining Time"
6
+ },
7
+ "seconds": {
8
+ "type": "number",
9
+ "default": 38
10
+ },
11
+ "endTime": {
12
+ "type": "number",
13
+ "default": 0
14
+ },
15
+ "format": {
16
+ "type": "enum",
17
+ "values": [
18
+ "auto",
19
+ "mm:ss",
20
+ "hh:mm:ss"
21
+ ],
22
+ "default": "auto"
23
+ }
24
+ },
25
+ "settings": {
26
+ "width": "200px",
27
+ "height": "72px"
28
+ },
29
+ "html": "<div class=\"kpi\"><span id=\"lbl\" class=\"lbl\"></span><span id=\"val\" class=\"val mono\"></span></div>",
30
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.kpi{height:100%;display:flex;flex-direction:column;justify-content:center;gap:2px;padding:10px 14px;border-radius:12px;background:var(--ui-surface-2,#3a414d);border:1px solid var(--ui-border,#4a5262)}\n.lbl{font-size:13px;color:var(--ui-dim,#9aa5b5)}.val{font-size:28px;font-weight:500}\n",
31
+ "script": "export function fmtDuration(sec, format){\n sec = Math.max(0, Math.floor(Number(sec) || 0));\n const h = Math.floor(sec / 3600), m = Math.floor(sec % 3600 / 60), s = sec % 60;\n const p = (n) => String(n).padStart(2, '0');\n if(format === 'hh:mm:ss' || (format !== 'mm:ss' && h > 0)) return p(h) + ':' + p(m) + ':' + p(s);\n return p(h * 60 + m) + ':' + p(s);\n}\nexport function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const left = () => {\n const end = Number(instance.endTime) || 0;\n return end > 0 ? (end - Date.now()) / 1000 : Number(instance.seconds) || 0;\n };\n const upd = () => { $('lbl').textContent = instance.label || ''; $('val').textContent = fmtDuration(left(), instance.format); };\n for(const p of ['label', 'seconds', 'end-time', 'format']) instance._assignEvent(p + '-changed', upd);\n clearInterval(instance.__cdTimer);\n // with an end time the tile counts down by itself\n instance.__cdTimer = setInterval(() => { if(Number(instance.endTime) > 0) upd(); }, 1000);\n upd();\n}\nexport function disconnectedCallback(instance){ clearInterval(instance.__cdTimer); }\n"
32
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "properties": {
3
+ "value": {
4
+ "type": "number",
5
+ "default": 3.2
6
+ },
7
+ "min": {
8
+ "type": "number",
9
+ "default": 0
10
+ },
11
+ "max": {
12
+ "type": "number",
13
+ "default": 6
14
+ },
15
+ "unit": {
16
+ "type": "string",
17
+ "default": "bar"
18
+ },
19
+ "label": {
20
+ "type": "string",
21
+ "default": ""
22
+ },
23
+ "decimals": {
24
+ "type": "number",
25
+ "default": 1
26
+ },
27
+ "zones": {
28
+ "type": "string",
29
+ "default": "0-4:ok,4-5:warn,5-6:error"
30
+ }
31
+ },
32
+ "settings": {
33
+ "width": "120px",
34
+ "height": "120px"
35
+ },
36
+ "html": "<svg id=\"s\" class=\"dial\" viewBox=\"0 0 120 120\"><circle cx=\"60\" cy=\"60\" r=\"56\" class=\"face\"/><g id=\"zones\"></g><g id=\"ticks\"></g><line id=\"nd\" x1=\"60\" y1=\"60\" x2=\"60\" y2=\"20\" class=\"nd\"/><circle cx=\"60\" cy=\"60\" r=\"5\" class=\"hub\"/><text id=\"v\" x=\"60\" y=\"88\" class=\"vt\">0</text><text id=\"u\" x=\"60\" y=\"101\" class=\"ut\"></text><text id=\"l\" x=\"60\" y=\"74\" class=\"lt\"></text></svg>",
37
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.dial{width:100%;height:100%;display:block}\n.face{fill:var(--ui-surface-2,#3a414d);stroke:var(--ui-border,#4a5262);stroke-width:2}\n.tk{stroke:var(--ui-dim,#9aa5b5);stroke-width:1.5}\n.nd{stroke:var(--ui-error,#ff5a6a);stroke-width:2.5;stroke-linecap:round;transition:transform .4s;transform-origin:60px 60px}\n.hub{fill:var(--ui-text,#e6ebf2)}\n.vt{font:600 15px 'IBM Plex Mono',monospace;fill:var(--ui-text,#e6ebf2);text-anchor:middle}\n.ut{font:11px 'IBM Plex Sans',sans-serif;fill:var(--ui-dim,#9aa5b5);text-anchor:middle}\n.lt{font:9px 'IBM Plex Sans',sans-serif;fill:var(--ui-dim,#9aa5b5);text-anchor:middle;letter-spacing:.04em}\n.z{fill:none;stroke-width:6}\n.z.ok{stroke:var(--ui-ok,#3ccf7a)}.z.warn{stroke:var(--ui-warn,#f2a531)}.z.error{stroke:var(--ui-error,#ff5a6a)}.z.info{stroke:var(--ui-info,#57b6ff)}\n",
38
+ "script": "export function parseZones(text){\n return String(text || '').split(',').map(s => s.trim()).filter(Boolean).map(s => {\n const m = /^(-?[\\d.]+)\\s*-\\s*(-?[\\d.]+)\\s*:\\s*(\\w+)$/.exec(s);\n return m ? { from: Number(m[1]), to: Number(m[2]), tone: m[3] } : null;\n }).filter(Boolean);\n}\nconst A0 = -135, SPAN = 270;\nfunction pt(a, r){ const t = (a - 90) * Math.PI / 180; return [60 + r * Math.cos(t), 60 + r * Math.sin(t)]; }\nfunction arc(a1, a2, r){ const [x1, y1] = pt(a1, r), [x2, y2] = pt(a2, r); return 'M' + x1.toFixed(2) + ' ' + y1.toFixed(2) + ' A' + r + ' ' + r + ' 0 ' + (a2 - a1 > 180 ? 1 : 0) + ' 1 ' + x2.toFixed(2) + ' ' + y2.toFixed(2); }\nexport function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const NS = 'http://www.w3.org/2000/svg';\n const upd = () => {\n const min = Number(instance.min) || 0, max = Number(instance.max) || 100, span = (max - min) || 1;\n const ang = (v) => A0 + Math.max(0, Math.min(1, (v - min) / span)) * SPAN;\n const z = $('zones'); z.textContent = '';\n for(const zn of parseZones(instance.zones)){ const p = document.createElementNS(NS, 'path'); p.setAttribute('d', arc(ang(zn.from), ang(zn.to), 48)); p.setAttribute('class', 'z ' + zn.tone); z.appendChild(p); }\n const t = $('ticks'); t.textContent = '';\n for(let i = 0; i <= 10; i++){ const a = A0 + i * SPAN / 10, [x1, y1] = pt(a, 41), [x2, y2] = pt(a, i % 5 ? 37 : 34); const l = document.createElementNS(NS, 'line'); l.setAttribute('x1', x1); l.setAttribute('y1', y1); l.setAttribute('x2', x2); l.setAttribute('y2', y2); l.setAttribute('class', 'tk'); t.appendChild(l); }\n const v = Number(instance.value);\n $('nd').style.transform = 'rotate(' + ang(isFinite(v) ? v : min) + 'deg)';\n $('v').textContent = isFinite(v) ? v.toFixed(Math.max(0, Number(instance.decimals) || 0)) : '—';\n $('u').textContent = instance.unit || ''; $('l').textContent = instance.label || '';\n };\n for(const p of ['value', 'min', 'max', 'unit', 'label', 'decimals', 'zones']) instance._assignEvent(p + '-changed', upd);\n upd();\n}\n"
39
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "properties": {
3
+ "label": {
4
+ "type": "string",
5
+ "default": "Accumulated Items"
6
+ },
7
+ "value": {
8
+ "type": "string",
9
+ "default": "1264"
10
+ },
11
+ "unit": {
12
+ "type": "string",
13
+ "default": ""
14
+ },
15
+ "decimals": {
16
+ "type": "number",
17
+ "default": -1
18
+ },
19
+ "alarmCount": {
20
+ "type": "number",
21
+ "default": 0
22
+ },
23
+ "ringMax": {
24
+ "type": "number",
25
+ "default": 0
26
+ },
27
+ "tone": {
28
+ "type": "enum",
29
+ "values": [
30
+ "ok",
31
+ "warn",
32
+ "error",
33
+ "info",
34
+ "accent",
35
+ "glow",
36
+ "off"
37
+ ],
38
+ "default": "warn"
39
+ },
40
+ "frame": {
41
+ "type": "boolean",
42
+ "default": "true"
43
+ }
44
+ },
45
+ "settings": {
46
+ "width": "330px",
47
+ "height": "72px"
48
+ },
49
+ "html": "<div id=\"k\" class=\"kpi\"><svg id=\"ring\" class=\"ring\" viewBox=\"0 0 40 40\" aria-hidden=\"true\"><circle cx=\"20\" cy=\"20\" r=\"15\" class=\"rt\"/><circle id=\"rv\" cx=\"20\" cy=\"20\" r=\"15\" class=\"rv\" transform=\"rotate(-90 20 20)\"/></svg><span id=\"lbl\" class=\"lbl\"></span><span class=\"val mono\"><span id=\"val\"></span><small id=\"unit\"></small></span><span id=\"al\" class=\"al\">🔔<span id=\"aln\"></span></span></div>",
50
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.kpi{height:100%;display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:12px;background:var(--ui-surface-2,#3a414d);border:1px solid var(--ui-border,#4a5262);min-width:0}\n.kpi.bare{background:none;border:0;padding:0}\n.ring{width:38px;height:38px;flex:none}.rt{fill:none;stroke:var(--ui-border,#4a5262);stroke-width:4}\n.rv{fill:none;stroke:var(--tone);stroke-width:4;stroke-linecap:round;stroke-dasharray:94.25;transition:stroke-dashoffset .4s}\n.lbl{font-size:13px;color:var(--ui-dim,#9aa5b5);line-height:1.3;white-space:pre-line}\n.val{margin-left:auto;font-size:30px;font-weight:500;white-space:nowrap}.val small{font-size:14px;color:var(--ui-dim,#9aa5b5);margin-left:4px}\n.al{display:flex;align-items:center;gap:3px;color:var(--ui-warn,#f2a531);font:600 13px 'IBM Plex Mono',monospace}\n",
51
+ "script": "export function fmtValue(v, decimals){\n const n = Number(v);\n if(v === null || v === undefined || v === '') return '—';\n if(!isFinite(n) || typeof v === 'boolean') return String(v);\n const d = Number(decimals);\n return d >= 0 ? n.toFixed(d) : String(Math.round(n * 1000) / 1000);\n}\nexport function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const upd = () => {\n $('k').className = 'kpi t-' + (instance.tone || 'warn') + ((instance.frame === false || instance.frame === 'false') ? ' bare' : '');\n $('lbl').textContent = instance.label || '';\n $('val').textContent = fmtValue(instance.value, instance.decimals);\n $('unit').textContent = instance.unit || '';\n const al = Number(instance.alarmCount) || 0;\n $('al').style.display = al > 0 ? '' : 'none';\n $('aln').textContent = String(al);\n const max = Number(instance.ringMax) || 0;\n $('ring').style.display = max > 0 ? '' : 'none';\n if(max > 0){ const p = Math.max(0, Math.min(1, (Number(instance.value) || 0) % (max + 1e-9) / max)); $('rv').style.strokeDashoffset = String(94.25 * (1 - p)); }\n };\n for(const p of ['label', 'value', 'unit', 'decimals', 'alarm-count', 'ring-max', 'tone', 'frame']) instance._assignEvent(p + '-changed', upd);\n upd();\n}\n"
52
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "properties": {
3
+ "gate": {
4
+ "type": "enum",
5
+ "values": [
6
+ "AND",
7
+ "OR",
8
+ "XOR",
9
+ "NAND",
10
+ "NOR",
11
+ "NOT",
12
+ "ARROW"
13
+ ],
14
+ "default": "AND"
15
+ },
16
+ "in1": {
17
+ "type": "boolean",
18
+ "default": "false"
19
+ },
20
+ "in2": {
21
+ "type": "boolean",
22
+ "default": "false"
23
+ },
24
+ "out": {
25
+ "type": "string",
26
+ "default": ""
27
+ },
28
+ "label": {
29
+ "type": "string",
30
+ "default": ""
31
+ }
32
+ },
33
+ "settings": {
34
+ "width": "150px",
35
+ "height": "50px"
36
+ },
37
+ "html": "<div id=\"g\" class=\"lg\"><i id=\"i1\" class=\"pin p1\"></i><i id=\"i2\" class=\"pin p2\"></i><span class=\"w w1\"></span><span class=\"w w2\"></span><span id=\"body\" class=\"gate\"></span><span class=\"w wo\"></span><i id=\"o\" class=\"pin po\"></i></div>",
38
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.lg{position:relative;height:100%}\n.gate{position:absolute;left:24px;right:24px;top:50%;height:34px;transform:translateY(-50%);border-radius:17px;display:grid;place-items:center;font:700 14px 'IBM Plex Sans',sans-serif;letter-spacing:.04em;\n background:linear-gradient(180deg,#f2f4f7,#c9cfd8);color:#2a313b;box-shadow:0 3px 8px rgba(0,0,0,.3)}\n.lg.on .gate{box-shadow:0 0 0 2px var(--db-glow),0 0 16px var(--db-glow)}\n.w{position:absolute;height:2px;background:var(--ui-border,#4a5262)}\n.w1{left:8px;width:16px;top:calc(50% - 9px)}.w2{left:8px;width:16px;top:calc(50% + 7px)}.wo{right:8px;width:16px;top:calc(50% - 1px)}\n.w.on{background:var(--db-glow);box-shadow:0 0 6px var(--db-glow)}\n.pin{position:absolute;width:9px;height:9px;border-radius:50%;border:2px solid var(--ui-dim,#9aa5b5);background:var(--ui-surface,#323843)}\n.p1{left:0;top:calc(50% - 13px)}.p2{left:0;top:calc(50% + 3px)}.po{right:0;top:calc(50% - 5px)}\n.pin.on{background:var(--db-glow);border-color:var(--db-glow);box-shadow:0 0 8px var(--db-glow)}\n.lg.one .p2,.lg.one .w2{display:none}.lg.one .w1{top:calc(50% - 1px)}.lg.one .p1{top:calc(50% - 5px)}\n",
39
+ "script": "export function evalGate(gate, a, b){\n switch(gate){\n case 'AND': return a && b;\n case 'OR': return a || b;\n case 'XOR': return a !== b;\n case 'NAND': return !(a && b);\n case 'NOR': return !(a || b);\n case 'NOT': return !a;\n default: return a; // ARROW passes its input on\n }\n}\nexport function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const yes = (v) => v === true || v === 1 || v === 'true' || v === '1';\n const upd = () => {\n const gate = instance.gate || 'AND', one = gate === 'NOT' || gate === 'ARROW';\n const a = yes(instance.in1), b = yes(instance.in2);\n const out = instance.out === '' || instance.out == null ? evalGate(gate, a, b) : yes(instance.out);\n $('body').textContent = instance.label || (gate === 'ARROW' ? '⇒' : gate);\n const g = $('g');\n g.classList.toggle('one', one); g.classList.toggle('on', out);\n $('i1').classList.toggle('on', a); $('i2').classList.toggle('on', b); $('o').classList.toggle('on', out);\n const w = g.querySelectorAll('.w'); w[0].classList.toggle('on', a); w[1].classList.toggle('on', b); w[2].classList.toggle('on', out);\n if(instance.__out !== out){ instance.__out = out; instance.dispatchEvent(new CustomEvent('output-changed', { detail: { value: out }, bubbles: true, composed: true })); }\n };\n for(const p of ['gate', 'in1', 'in2', 'out', 'label']) instance._assignEvent(p + '-changed', upd);\n upd();\n}\n"
40
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "properties": {
3
+ "count": {
4
+ "type": "number",
5
+ "default": 4
6
+ },
7
+ "screen": {
8
+ "type": "screen",
9
+ "default": ""
10
+ }
11
+ },
12
+ "settings": {
13
+ "width": "40px",
14
+ "height": "40px"
15
+ },
16
+ "html": "<button id=\"b\" class=\"ib\" type=\"button\" aria-label=\"Notifications\"><svg class=\"bi\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 22a2.5 2.5 0 0 0 2.4-2h-4.8A2.5 2.5 0 0 0 12 22zm7-6V11a7 7 0 0 0-5.5-6.8V3.5a1.5 1.5 0 0 0-3 0v.7A7 7 0 0 0 5 11v5l-2 2v1h18v-1z\"/></svg><span id=\"n\" class=\"bdg mono\"></span></button>",
17
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.ib{width:100%;height:100%;border-radius:9px;display:grid;place-items:center;background:transparent;border:0;color:var(--ui-dim,#9aa5b5);cursor:pointer;position:relative}\n.ib:hover{color:var(--ui-text,#e6ebf2)}.bi{width:60%;height:60%}\n.bdg{position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:var(--ui-error,#ff5a6a);color:#fff;font-size:10px;font-weight:700;display:grid;place-items:center}\n.ib:focus-visible{outline:2px solid var(--accent,#57b6ff)}\n",
18
+ "script": "export function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const upd = () => { const n = Number(instance.count) || 0; $('n').textContent = n > 99 ? '99+' : String(n); $('n').style.display = n > 0 ? '' : 'none'; };\n $('b').addEventListener('click', () => {\n if(instance.screen && window.IOB && window.IOB.changeView) window.IOB.changeView.emit(instance.screen);\n instance.dispatchEvent(new CustomEvent('bellclick', { bubbles: true, composed: true }));\n });\n instance._assignEvent('count-changed', upd);\n upd();\n}\n"
19
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "properties": {
3
+ "title": {
4
+ "type": "string",
5
+ "default": "Panel"
6
+ },
7
+ "activeDot": {
8
+ "type": "number",
9
+ "default": 1
10
+ },
11
+ "showMenu": {
12
+ "type": "boolean",
13
+ "default": "true"
14
+ },
15
+ "direction": {
16
+ "type": "enum",
17
+ "values": [
18
+ "row",
19
+ "column"
20
+ ],
21
+ "default": "row"
22
+ },
23
+ "gap": {
24
+ "type": "string",
25
+ "default": "12px"
26
+ },
27
+ "align": {
28
+ "type": "enum",
29
+ "values": [
30
+ "stretch",
31
+ "flex-start",
32
+ "center",
33
+ "flex-end"
34
+ ],
35
+ "default": "center"
36
+ }
37
+ },
38
+ "settings": {
39
+ "width": "600px",
40
+ "height": "360px"
41
+ },
42
+ "html": "<div class=\"ch\"><span id=\"ttl\"></span><span id=\"dots\" class=\"dots\" title=\"Menu\"><i></i><i></i><i></i></span></div><slot></slot>",
43
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n:host{display:flex;flex-wrap:wrap;align-content:flex-start;align-items:center;gap:12px;padding:0 16px 16px;position:relative;min-width:0;\n border-radius:14px;border:1px solid var(--ui-border,#4a5262);\n background:linear-gradient(180deg,color-mix(in srgb,var(--ui-surface,#323843) 92%,#fff 3%),var(--ui-surface,#323843));box-shadow:0 10px 30px rgba(0,0,0,.25)}\n.ch{flex:0 0 calc(100% + 32px);align-self:stretch;margin:0 -16px 4px;display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--ui-border,#4a5262);font-weight:500;font-size:16px}\n.dots{display:flex;gap:5px;cursor:pointer;padding:4px}.dots i{width:6px;height:6px;border-radius:50%;background:var(--ui-dim,#9aa5b5);opacity:.6}\n.dots i.on{background:var(--db-glow);opacity:1;box-shadow:0 0 6px var(--db-glow)}\n::slotted(*){max-width:100%;min-width:0}\n",
44
+ "script": "export function init(instance){\n const upd = () => {\n instance._getDomElement('ttl').textContent = instance.title || '';\n const dots = instance._getDomElement('dots');\n dots.style.display = (instance.showMenu === false || instance.showMenu === 'false') ? 'none' : '';\n const a = Number(instance.activeDot) || 0;\n [...dots.children].forEach((d, i) => d.classList.toggle('on', i + 1 === a));\n const col = instance.direction === 'column';\n instance.style.flexDirection = col ? 'column' : 'row';\n instance.style.flexWrap = col ? 'nowrap' : 'wrap';\n instance.style.gap = instance.gap || '12px';\n instance.style.alignItems = col ? 'stretch' : (instance.align || 'center');\n };\n instance._getDomElement('dots').addEventListener('click', () =>\n instance.dispatchEvent(new CustomEvent('menu', { bubbles: true, composed: true })));\n for(const p of ['title', 'active-dot', 'show-menu', 'direction', 'gap', 'align']) instance._assignEvent(p + '-changed', upd);\n upd();\n // at runtime the controls inside flow: a position left over from the designer's free\n // placement is dropped (the designer keeps it while editing)\n if(!document.querySelector('iobroker-webui-app-shell')){\n const flow = () => { for(const c of instance.children) if(c.style && c.style.position === 'absolute'){ c.style.position = ''; c.style.left = ''; c.style.top = ''; } };\n flow();\n instance.__pcMo = new MutationObserver(flow);\n instance.__pcMo.observe(instance, { childList: true });\n }\n}\nexport function disconnectedCallback(instance){ try{ instance.__pcMo && instance.__pcMo.disconnect(); }catch(e){} }\n"
45
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "properties": {
3
+ "label": {
4
+ "type": "string",
5
+ "default": "Main motor"
6
+ },
7
+ "signal": {
8
+ "type": "signal",
9
+ "default": ""
10
+ },
11
+ "signalHigh": {
12
+ "type": "signal",
13
+ "default": ""
14
+ },
15
+ "min": {
16
+ "type": "number",
17
+ "default": 1000
18
+ },
19
+ "max": {
20
+ "type": "number",
21
+ "default": 1400
22
+ },
23
+ "step": {
24
+ "type": "number",
25
+ "default": 10
26
+ },
27
+ "unit": {
28
+ "type": "string",
29
+ "default": ""
30
+ },
31
+ "decimals": {
32
+ "type": "number",
33
+ "default": 0
34
+ },
35
+ "showLabel": {
36
+ "type": "boolean",
37
+ "default": "true"
38
+ }
39
+ },
40
+ "settings": {
41
+ "width": "520px",
42
+ "height": "86px"
43
+ },
44
+ "html": "<div class=\"sld\"><span id=\"nm\" class=\"nm\"></span><div id=\"tr\" class=\"track\"><div class=\"ticks\"></div><div id=\"fill\" class=\"fill\"></div><div id=\"t1\" class=\"thumb\" tabindex=\"0\" role=\"slider\"><span class=\"bub mono\"></span></div><div id=\"t2\" class=\"thumb\" tabindex=\"0\" role=\"slider\"><span class=\"bub mono\"></span></div></div><div class=\"mm mono\"><span id=\"mn\"></span><span id=\"mx\"></span></div></div>",
45
+ "style": ":host{display:block;box-sizing:border-box;font-family:'IBM Plex Sans','Segoe UI',system-ui,sans-serif;color:var(--ui-text,#e6ebf2);--db-glow:var(--ui-glow,#ffd36a)}\n*{box-sizing:border-box}\n.mono{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}\n@media (prefers-reduced-motion: reduce){*{animation:none!important}}\n.t-ok{--tone:var(--ui-ok,#3ccf7a)} .t-warn{--tone:var(--ui-warn,#f2a531)} .t-error{--tone:var(--ui-error,#ff5a6a)}\n.t-info{--tone:var(--ui-info,#57b6ff)} .t-accent{--tone:var(--accent,#57b6ff)} .t-glow{--tone:var(--db-glow)} .t-off{--tone:var(--ui-dim,#9aa5b5)}\n.sld{height:100%;display:grid;grid-template-columns:auto minmax(0,1fr);gap:2px 12px;align-items:center;padding-top:22px;box-sizing:border-box}\n.sld.nolabel{grid-template-columns:minmax(0,1fr)}.sld.nolabel .nm{display:none}.sld.nolabel .mm{grid-column:1}\n.nm{font-size:14px;color:var(--ui-dim,#9aa5b5)}\n.track{position:relative;height:30px;margin:0 11px}\n.ticks{position:absolute;left:0;right:0;top:4px;height:16px;background:repeating-linear-gradient(90deg,var(--ui-border,#4a5262) 0 1px,transparent 1px 8px)}\n.ticks::after{content:\"\";position:absolute;inset:0;background:repeating-linear-gradient(90deg,var(--ui-dim,#9aa5b5) 0 1px,transparent 1px 40px);opacity:.6}\n.fill{position:absolute;top:11px;height:3px;border-radius:2px;background:var(--accent,#57b6ff);box-shadow:0 0 8px var(--accent,#57b6ff)}\n.thumb{position:absolute;top:2px;width:22px;height:22px;margin-left:-11px;border-radius:50%;cursor:grab;touch-action:none;\n background:radial-gradient(circle,#fff 30%,color-mix(in srgb,var(--accent,#57b6ff) 60%,#fff));border:3px solid var(--accent,#57b6ff);box-shadow:0 0 12px var(--accent,#57b6ff)}\n.thumb:focus-visible{outline:2px solid var(--ui-text,#fff);outline-offset:2px}\n.bub{position:absolute;bottom:26px;left:50%;transform:translateX(-50%);font-size:11px;font-weight:600;background:var(--ui-surface-2,#3a414d);border:1px solid var(--ui-border,#4a5262);border-radius:5px;padding:1px 5px;white-space:nowrap}\n.mm{grid-column:2;display:flex;justify-content:space-between;font-size:12px;color:var(--ui-dim,#9aa5b5);margin:0 4px}\n",
46
+ "script": "function dbSubscribe(instance, key, id, onVal){\n dbUnsub(instance, key);\n if(!id || !window.IOB) return;\n instance.__dbSubs = instance.__dbSubs || {};\n const cb = function(_i, s){ onVal(s ? s.val : null); };\n instance.__dbSubs[key] = [id, cb];\n try{ window.IOB.subscribeState(id, cb); }catch(e){}\n try{ window.IOB.getState(id).then(function(s){ onVal(s ? s.val : null); }).catch(function(){}); }catch(e){}\n // read quality knows this state (badge on the control when the value cannot be trusted)\n try{ instance.__dbRq = instance.__dbRq || {}; instance.__dbRq[key] = window.IOB.watchReadQuality && window.IOB.watchReadQuality(instance, id, { prop: key, twoWay: true }); }catch(e){}\n}\nfunction dbUnsub(instance, key){\n const p = instance.__dbSubs && instance.__dbSubs[key];\n if(p){ try{ window.IOB.unsubscribeState(p[0], p[1]); }catch(e){} delete instance.__dbSubs[key]; }\n const r = instance.__dbRq && instance.__dbRq[key];\n if(typeof r === 'function'){ try{ r(); }catch(e){} }\n if(instance.__dbRq) delete instance.__dbRq[key];\n}\nfunction dbUnsubAll(instance){ for(const k of Object.keys(instance.__dbSubs || {})) dbUnsub(instance, k); }\n// a write through IOB.setState: Confirm & Protect may ask first; cancelled = nothing written\nfunction dbWrite(instance, id, val, onCancel){\n if(!id || !window.IOB) return Promise.resolve(false);\n return Promise.resolve(window.IOB.setState(id, val, false, { feedback: instance })).then(function(){ return true; }, function(e){\n if(e && e.cancelled){ if(onCancel) onCancel(); }\n else console.warn('[dashboard] write failed', id, e);\n return false;\n });\n}\nfunction dbBool(v){ return v === true || v === 1 || v === 'true' || v === '1' || v === 'on'; }\nfunction dbNum(v, d){ const n = Number(v); return isFinite(n) ? n : d; }\nexport function snap(v, min, max, step){\n v = Math.min(max, Math.max(min, Number(v)));\n if(step > 0) v = min + Math.round((v - min) / step) * step;\n return Math.round(Math.min(max, Math.max(min, v)) * 1e6) / 1e6;\n}\n/** the two thumbs never cross: the moved one stops one step before the other */\nexport function orderRange(lo, hi, moved, step){\n const gap = step > 0 ? step : 0;\n if(moved === 'lo') return [Math.min(lo, hi - gap), hi];\n return [lo, Math.max(hi, lo + gap)];\n}\nexport function init(instance){\n const $ = (id) => instance._getDomElement(id);\n const st = { lo: null, hi: null };\n const range = () => !!instance.signalHigh;\n const P = () => ({ min: dbNum(instance.min, 0), max: dbNum(instance.max, 100), step: dbNum(instance.step, 1) });\n const draw = () => {\n const { min, max } = P(), dec = Math.max(0, dbNum(instance.decimals, 0));\n const pct = (v) => ((dbNum(v, min) - min) / ((max - min) || 1)) * 100;\n $('nm').textContent = instance.label || '';\n $('nm').parentElement.classList.toggle('nolabel', instance.showLabel === false || instance.showLabel === 'false');\n $('mn').textContent = String(min); $('mx').textContent = String(max);\n const lo = st.lo ?? min, hi = st.hi ?? max;\n for(const [t, v] of [[$('t1'), lo], [$('t2'), hi]]){\n t.style.left = pct(v) + '%';\n t.querySelector('.bub').textContent = dbNum(v, min).toFixed(dec) + (instance.unit ? ' ' + instance.unit : '');\n t.setAttribute('aria-valuenow', String(v)); t.setAttribute('aria-valuemin', String(min)); t.setAttribute('aria-valuemax', String(max));\n }\n $('t2').style.display = range() ? '' : 'none';\n const f = $('fill');\n if(range()){ f.style.left = pct(lo) + '%'; f.style.width = Math.max(0, pct(hi) - pct(lo)) + '%'; }\n else { f.style.left = '0'; f.style.width = pct(lo) + '%'; }\n };\n const idOf = (k) => k === 'lo' ? instance.signal : instance.signalHigh;\n const refresh = (k) => { const id = idOf(k); if(id) window.IOB.getState(id).then(s => { st[k] = s ? dbNum(s.val, null) : null; draw(); }).catch(() => {}); };\n const setV = (k, v) => {\n const { min, max, step } = P();\n st[k] = snap(v, min, max, step);\n if(range()){ const [a, b] = orderRange(st.lo ?? min, st.hi ?? max, k, step); st.lo = a; st.hi = b; }\n draw();\n };\n const commit = (k) => dbWrite(instance, idOf(k), st[k], () => refresh(k));\n for(const [el, k] of [[$('t1'), 'lo'], [$('t2'), 'hi']]){\n el.addEventListener('pointerdown', (e) => {\n el.setPointerCapture(e.pointerId);\n const { min, max } = P();\n const mv = (ev) => { const r = $('tr').getBoundingClientRect(); setV(k, min + (ev.clientX - r.left) / (r.width || 1) * (max - min)); };\n const up = () => { el.removeEventListener('pointermove', mv); el.removeEventListener('pointerup', up); commit(k); };\n el.addEventListener('pointermove', mv); el.addEventListener('pointerup', up);\n });\n el.addEventListener('keydown', (e) => {\n const { step, min, max } = P(), d = step > 0 ? step : (max - min) / 100;\n if(e.key === 'ArrowRight' || e.key === 'ArrowUp'){ setV(k, (st[k] ?? min) + d); commit(k); e.preventDefault(); }\n if(e.key === 'ArrowLeft' || e.key === 'ArrowDown'){ setV(k, (st[k] ?? min) - d); commit(k); e.preventDefault(); }\n });\n }\n const sub = () => {\n dbSubscribe(instance, 'signal', instance.signal, (v) => { st.lo = dbNum(v, null); draw(); });\n if(instance.signalHigh) dbSubscribe(instance, 'signalHigh', instance.signalHigh, (v) => { st.hi = dbNum(v, null); draw(); });\n else dbUnsub(instance, 'signalHigh');\n draw();\n };\n for(const p of ['label', 'min', 'max', 'step', 'unit', 'decimals', 'show-label']) instance._assignEvent(p + '-changed', draw);\n instance._assignEvent('signal-changed', sub); instance._assignEvent('signal-high-changed', sub);\n sub();\n}\nexport function disconnectedCallback(instance){ dbUnsubAll(instance); }\n"
47
+ }