1
0

settings.html 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603
  1. {% extends "base.html" %} {% block title %}Settings - {{ app_name or 'Dune Weaver' }}{%
  2. endblock %}
  3. {% block additional_styles %}
  4. /* Dark mode styles for settings page */
  5. .dark .bg-white {
  6. background-color: #262626;
  7. }
  8. .dark .text-slate-900 {
  9. color: #ffffff;
  10. }
  11. .dark .text-slate-800 {
  12. color: #f8fafc;
  13. }
  14. .dark .text-slate-700 {
  15. color: #f1f5f9;
  16. }
  17. .dark .text-slate-600 {
  18. color: #e2e8f0;
  19. }
  20. .dark .text-slate-500 {
  21. color: #e2e8f0;
  22. }
  23. /* Label overrides for better visibility */
  24. .dark label {
  25. color: #f1f5f9;
  26. }
  27. .dark .border-slate-200 {
  28. border-color: #404040;
  29. }
  30. .dark .border-slate-300 {
  31. border-color: #404040;
  32. }
  33. .dark .divide-slate-100 {
  34. border-color: #333333;
  35. }
  36. .dark .bg-slate-50 {
  37. background-color: #262626;
  38. }
  39. .dark .hover\:bg-slate-50:hover {
  40. background-color: #404040;
  41. }
  42. .dark .bg-slate-100 {
  43. background-color: #404040;
  44. }
  45. .dark .form-input,
  46. .dark input[type="number"],
  47. .dark input[type="text"],
  48. .dark input[type="time"] {
  49. background-color: #1f1f1f;
  50. border-color: #404040;
  51. color: #e5e5e5;
  52. }
  53. .dark .form-input::placeholder {
  54. color: #9ca3af;
  55. }
  56. .dark .form-input:focus {
  57. border-color: #0c7ff2;
  58. ring-color: #0c7ff2;
  59. }
  60. .dark input[type="time"]::-webkit-calendar-picker-indicator {
  61. filter: invert(1);
  62. }
  63. .dark .form-select {
  64. background-color: #1f1f1f;
  65. border-color: #404040;
  66. color: #e5e5e5;
  67. }
  68. .dark .form-select:focus {
  69. border-color: #0c7ff2;
  70. ring-color: #0c7ff2;
  71. }
  72. .dark .focus\:ring-sky-500:focus {
  73. ring-color: #0c7ff2;
  74. }
  75. .dark .focus\:border-sky-500:focus {
  76. border-color: #0c7ff2;
  77. }
  78. .dark .hover\:text-gray-700:hover {
  79. color: #e5e5e5;
  80. }
  81. .dark .text-gray-400 {
  82. color: #9ca3af;
  83. }
  84. /* Autocomplete suggestions dark mode */
  85. .dark #clearFromInSuggestions,
  86. .dark #clearFromOutSuggestions {
  87. background-color: #262626;
  88. border-color: #404040;
  89. }
  90. .dark .suggestion-item {
  91. color: #e5e5e5;
  92. }
  93. .dark .suggestion-item:hover {
  94. background-color: #404040;
  95. }
  96. .dark .suggestion-item.selected {
  97. background-color: #0c7ff2;
  98. color: white;
  99. }
  100. /* Light mode autocomplete styles */
  101. .suggestion-item {
  102. padding: 8px 12px;
  103. cursor: pointer;
  104. color: #1f2937;
  105. transition: background-color 0.15s;
  106. }
  107. .suggestion-item:hover {
  108. background-color: #f3f4f6;
  109. }
  110. .suggestion-item.selected {
  111. background-color: #0c7ff2;
  112. color: white;
  113. }
  114. .suggestion-item mark {
  115. background-color: #fef3c7;
  116. font-weight: 600;
  117. }
  118. .dark .suggestion-item mark {
  119. background-color: #92400e;
  120. color: #fef3c7;
  121. }
  122. /* Toggle switch styles */
  123. .switch {
  124. position: relative;
  125. display: inline-block;
  126. width: 60px;
  127. height: 34px;
  128. }
  129. .switch input {
  130. opacity: 0;
  131. width: 0;
  132. height: 0;
  133. }
  134. .slider {
  135. position: absolute;
  136. cursor: pointer;
  137. top: 0;
  138. left: 0;
  139. right: 0;
  140. bottom: 0;
  141. background-color: #ccc;
  142. transition: .4s;
  143. }
  144. .slider:before {
  145. position: absolute;
  146. content: "";
  147. height: 26px;
  148. width: 26px;
  149. left: 4px;
  150. bottom: 4px;
  151. background-color: white;
  152. transition: .4s;
  153. }
  154. input:checked + .slider {
  155. background-color: #0c7ff2;
  156. }
  157. input:focus + .slider {
  158. box-shadow: 0 0 1px #0c7ff2;
  159. }
  160. input:checked + .slider:before {
  161. transform: translateX(26px);
  162. }
  163. .slider.round {
  164. border-radius: 34px;
  165. }
  166. .slider.round:before {
  167. border-radius: 50%;
  168. }
  169. /* Dark mode for switches */
  170. .dark .slider {
  171. background-color: #404040;
  172. }
  173. .dark input:checked + .slider {
  174. background-color: #0c7ff2;
  175. }
  176. /* Spin animation for loading states */
  177. @keyframes spin {
  178. from {
  179. transform: rotate(0deg);
  180. }
  181. to {
  182. transform: rotate(360deg);
  183. }
  184. }
  185. .animate-spin {
  186. animation: spin 1s linear infinite;
  187. }
  188. /* Collapsible section styles */
  189. .section-header {
  190. cursor: pointer;
  191. user-select: none;
  192. display: flex;
  193. justify-content: space-between;
  194. align-items: center;
  195. }
  196. .section-header:hover {
  197. background-color: #f8fafc;
  198. }
  199. .dark .section-header:hover {
  200. background-color: #333333;
  201. }
  202. .section-toggle-icon {
  203. transition: transform 0.2s ease-in-out;
  204. }
  205. .section-header.collapsed .section-toggle-icon {
  206. transform: rotate(-90deg);
  207. }
  208. .section-header.collapsed {
  209. border-bottom: none;
  210. }
  211. .section-content {
  212. overflow: hidden;
  213. transition: max-height 0.3s ease-in-out, opacity 0.2s ease-in-out, padding 0.2s ease-in-out;
  214. max-height: 2000px;
  215. opacity: 1;
  216. }
  217. .section-content.collapsed {
  218. max-height: 0;
  219. opacity: 0;
  220. padding-top: 0 !important;
  221. padding-bottom: 0 !important;
  222. }
  223. /* Time slot specific styles */
  224. .time-slot-item {
  225. background-color: #f8fafc;
  226. border: 1px solid #e2e8f0;
  227. border-radius: 8px;
  228. padding: 16px;
  229. transition: all 0.15s;
  230. }
  231. .dark .time-slot-item {
  232. background-color: #1e293b;
  233. border-color: #475569;
  234. }
  235. .time-slot-item:hover {
  236. border-color: #cbd5e1;
  237. }
  238. .dark .time-slot-item:hover {
  239. border-color: #64748b;
  240. }
  241. /* Info box dark mode - grey theme */
  242. .dark .bg-blue-50 {
  243. background-color: #1f1f1f;
  244. }
  245. .dark .border-blue-200 {
  246. border-color: #404040;
  247. }
  248. .dark .text-blue-600 {
  249. color: #e2e8f0;
  250. }
  251. .dark .text-blue-800 {
  252. color: #f1f5f9;
  253. }
  254. .dark .text-blue-700 {
  255. color: #e2e8f0;
  256. }
  257. /* Amber box dark mode - grey theme */
  258. .dark .bg-amber-50 {
  259. background-color: #1f1f1f;
  260. }
  261. .dark .border-amber-200 {
  262. border-color: #404040;
  263. }
  264. .dark .text-amber-600 {
  265. color: #f1f5f9;
  266. }
  267. /* Sky box dark mode - grey theme (Still Sands options) */
  268. .dark .bg-sky-50 {
  269. background-color: #1f1f1f;
  270. }
  271. .dark .border-sky-200 {
  272. border-color: #404040;
  273. }
  274. /* Select dropdown dark mode */
  275. .dark select {
  276. background-color: #1f1f1f;
  277. border-color: #404040;
  278. color: #e5e5e5;
  279. }
  280. .dark select:focus {
  281. border-color: #0c7ff2;
  282. }
  283. .dark select option {
  284. background-color: #1f1f1f;
  285. color: #e5e5e5;
  286. }
  287. .dark select optgroup {
  288. background-color: #262626;
  289. color: #9ca3af;
  290. }
  291. {% endblock %}
  292. {% block content %}
  293. <div class="layout-content-container flex flex-col w-full max-w-4xl gap-8 pt-2 pb-[75px]">
  294. <div
  295. class="flex flex-wrap justify-between items-center p-4 bg-white rounded-xl shadow-sm mt-2 sm:mt-8"
  296. >
  297. <h1
  298. class="text-slate-900 tracking-tight text-2xl sm:text-3xl font-bold leading-tight"
  299. >
  300. Settings
  301. </h1>
  302. </div>
  303. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  304. <h2
  305. class="section-header text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  306. onclick="toggleSection(this)"
  307. >
  308. <span>Device Connection</span>
  309. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  310. </h2>
  311. <div class="section-content">
  312. <div
  313. class="flex items-center gap-4 px-6 py-5 hover:bg-slate-50 transition-colors"
  314. >
  315. <div
  316. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  317. >
  318. <span class="material-icons text-3xl">usb_off</span>
  319. </div>
  320. <div class="flex-1">
  321. <p class="text-slate-800 text-base font-medium leading-normal">
  322. Status
  323. </p>
  324. <p
  325. id="serialStatus"
  326. class="text-red-500 text-sm font-medium leading-normal"
  327. >
  328. Disconnected
  329. </p>
  330. </div>
  331. <button
  332. id="disconnectButton"
  333. class="text-xs font-medium bg-red-100 hover:bg-red-200 text-red-700 dark:bg-red-900 dark:hover:bg-red-800 dark:text-red-200 px-3 py-1.5 rounded-md transition-colors"
  334. hidden
  335. >
  336. Disconnect
  337. </button>
  338. </div>
  339. <div id="portSelectionDiv" class="px-6 py-5 space-y-4">
  340. <label class="flex flex-col gap-1.5">
  341. <span class="text-slate-700 text-sm font-medium leading-normal"
  342. >Available Serial Ports</span
  343. >
  344. <div class="flex gap-3 items-center">
  345. <select
  346. id="portSelect"
  347. class="form-select flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors "
  348. >
  349. <option value="">Select a port...</option>
  350. </select>
  351. <button
  352. id="connectButton"
  353. class="flex items-center justify-center gap-2 min-w-[100px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors flex-shrink-0"
  354. >
  355. <span class="material-icons text-lg">cable</span>
  356. <span class="truncate">Connect</span>
  357. </button>
  358. </div>
  359. <p class="text-xs text-slate-500 mt-2">
  360. Select a port and click 'Connect' to establish a connection.
  361. </p>
  362. </label>
  363. </div>
  364. <!-- Preferred Port Configuration -->
  365. <div class="px-6 py-5">
  366. <label class="flex flex-col gap-1.5">
  367. <span class="text-slate-700 text-sm font-medium leading-normal flex items-center gap-2">
  368. <span class="material-icons text-slate-600 text-base">star</span>
  369. Preferred Port for Auto-Connect
  370. </span>
  371. <div class="flex gap-3 items-center">
  372. <select
  373. id="preferredPortSelect"
  374. class="form-select flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  375. >
  376. <option value="">No preference (auto-detect)</option>
  377. </select>
  378. <button
  379. id="savePreferredPort"
  380. class="flex items-center justify-center gap-2 min-w-[100px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors flex-shrink-0"
  381. >
  382. <span class="material-icons text-lg">save</span>
  383. <span class="truncate">Save</span>
  384. </button>
  385. </div>
  386. <p class="text-xs text-slate-500 mt-2">
  387. When multiple ports are available, this port will be used automatically on startup. If set to "No preference", the system will try the last connected port or the first available port.
  388. </p>
  389. <p id="currentPreferredPort" class="text-xs text-sky-600 mt-1 hidden">
  390. <span class="material-icons text-xs align-middle">check_circle</span>
  391. <span id="preferredPortDisplay"></span>
  392. </p>
  393. </label>
  394. </div>
  395. </div>
  396. </section>
  397. <!-- Machine Settings Section -->
  398. <section id="machineSection" class="bg-white rounded-xl shadow-sm overflow-hidden">
  399. <h2
  400. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  401. onclick="toggleSection(this)"
  402. >
  403. <span>Machine Settings</span>
  404. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  405. </h2>
  406. <div class="section-content collapsed px-6 py-5 space-y-6">
  407. <!-- Table Type Override -->
  408. <div class="space-y-3">
  409. <label class="text-sm font-medium text-slate-700 flex items-center gap-2">
  410. <span class="material-icons text-slate-600 text-base">precision_manufacturing</span>
  411. Table Type
  412. </label>
  413. <!-- Current detected type display -->
  414. <div id="detectedTableTypeContainer" class="flex items-center gap-2 text-sm text-slate-600 bg-slate-50 rounded-lg p-3">
  415. <span class="material-icons text-slate-500 text-base">info</span>
  416. <span>Detected: <span id="detectedTableType" class="font-medium text-slate-800">Unknown</span></span>
  417. </div>
  418. <div class="flex gap-3 items-center">
  419. <select
  420. id="tableTypeSelect"
  421. class="form-select flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  422. >
  423. <option value="">Auto-detect (use detected type)</option>
  424. </select>
  425. <button
  426. id="saveTableType"
  427. class="flex items-center justify-center gap-2 min-w-[100px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors flex-shrink-0"
  428. >
  429. <span class="material-icons text-lg">save</span>
  430. <span class="truncate">Save</span>
  431. </button>
  432. </div>
  433. <p class="text-xs text-slate-500">
  434. Override the automatically detected table type. This affects gear ratio calculations and homing behavior. Leave as "Auto-detect" unless you need to manually specify your table type.
  435. </p>
  436. </div>
  437. <!-- Info box -->
  438. <div class="text-xs text-slate-600 bg-blue-50 border border-blue-200 rounded-lg p-3">
  439. <div class="flex items-start gap-2">
  440. <span class="material-icons text-blue-600 text-base">info</span>
  441. <div>
  442. <p class="font-medium text-blue-800">Table Type Detection</p>
  443. <ul class="mt-1 space-y-1 text-blue-700">
  444. <li>• Table type is normally detected automatically from GRBL settings</li>
  445. <li>• Use override if auto-detection is incorrect for your hardware</li>
  446. <li>• Changes take effect on next connection/homing</li>
  447. </ul>
  448. </div>
  449. </div>
  450. </div>
  451. </div>
  452. </section>
  453. <!-- Homing Configuration Section -->
  454. <section id="homingSection" class="bg-white rounded-xl shadow-sm overflow-hidden">
  455. <h2
  456. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  457. onclick="toggleSection(this)"
  458. >
  459. <span>Homing Configuration</span>
  460. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  461. </h2>
  462. <div class="section-content collapsed px-6 py-5 space-y-6">
  463. <!-- Homing Mode Selection -->
  464. <div class="space-y-3">
  465. <label class="text-sm font-medium text-slate-700 flex items-center gap-2">
  466. <span class="material-icons text-slate-600 text-base">home</span>
  467. Homing Mode
  468. </label>
  469. <div class="space-y-3">
  470. <!-- Crash Homing Option -->
  471. <label class="flex items-start gap-3 p-3 border border-slate-300 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors">
  472. <input
  473. type="radio"
  474. name="homingMode"
  475. value="0"
  476. id="homingModeCrash"
  477. class="mt-0.5 w-4 h-4 text-sky-600 focus:ring-sky-500"
  478. />
  479. <div class="flex-1">
  480. <div class="text-sm font-medium text-slate-700">Crash Homing</div>
  481. <div class="text-xs text-slate-500 mt-1">
  482. Y axis moves until physical stop, then theta and rho set to 0 (no x0 y0 command)
  483. </div>
  484. </div>
  485. </label>
  486. <!-- Sensor Homing Option -->
  487. <label class="flex items-start gap-3 p-3 border border-slate-300 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors">
  488. <input
  489. type="radio"
  490. name="homingMode"
  491. value="1"
  492. id="homingModeSensor"
  493. class="mt-0.5 w-4 h-4 text-sky-600 focus:ring-sky-500"
  494. />
  495. <div class="flex-1">
  496. <div class="text-sm font-medium text-slate-700">Sensor Homing</div>
  497. <div class="text-xs text-slate-500 mt-1">
  498. Homes both X and Y axes using sensors
  499. </div>
  500. </div>
  501. </label>
  502. </div>
  503. </div>
  504. <!-- Compass Reference Point (Sensor mode only) -->
  505. <div id="compassOffsetContainer" class="space-y-2">
  506. <label for="angularOffsetInput" class="text-sm font-medium text-slate-700 flex items-center gap-2">
  507. <span class="material-icons text-slate-600 text-base">explore</span>
  508. Sensor offset (degrees) <span class="text-xs text-slate-400">(Sensor mode only)</span>
  509. </label>
  510. <input
  511. type="number"
  512. id="angularOffsetInput"
  513. min="0"
  514. max="360"
  515. step="0.1"
  516. value="0"
  517. class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500 text-sm"
  518. placeholder="0.0"
  519. />
  520. <p class="text-xs text-slate-500">
  521. Set the angle (in degrees) where your radial arm to be offset by. You want to choose a value here so that the radial arm will point East.
  522. </p>
  523. </div>
  524. <!-- Homing Info Box -->
  525. <div id="homingInfoBox" class="text-xs text-slate-600 bg-blue-50 border border-blue-200 rounded-lg p-3">
  526. <div class="flex items-start gap-2">
  527. <span class="material-icons text-blue-600 text-base">info</span>
  528. <div id="homingInfoContent">
  529. <p class="font-medium text-blue-800">Crash Homing Mode:</p>
  530. <ul class="mt-1 space-y-1 text-blue-700">
  531. <li>• Y axis moves -22mm (or -30mm for mini) until physical stop</li>
  532. <li>• Theta set to 0, rho set to 0</li>
  533. <li>• No x0 y0 command sent</li>
  534. <li>• No hardware sensors required</li>
  535. </ul>
  536. </div>
  537. </div>
  538. </div>
  539. <!-- Auto-Home During Playlists -->
  540. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  541. <div class="flex items-center justify-between">
  542. <div class="flex-1">
  543. <h3 class="text-slate-800 text-base font-semibold flex items-center gap-2">
  544. <span class="material-icons text-slate-600 text-base">autorenew</span>
  545. Auto-Home During Playlists
  546. </h3>
  547. <p class="text-xs text-slate-500 mt-1">
  548. Automatically perform homing after a certain number of patterns during playlist playback to maintain accuracy.
  549. </p>
  550. </div>
  551. <label class="switch">
  552. <input type="checkbox" id="autoHomeEnabledToggle">
  553. <span class="slider round"></span>
  554. </label>
  555. </div>
  556. <div id="autoHomeSettings" style="display: none;">
  557. <label class="flex flex-col gap-1.5">
  558. <span class="text-slate-700 text-sm font-medium leading-normal">Home after every X patterns</span>
  559. <input
  560. type="number"
  561. id="autoHomeAfterPatternsInput"
  562. min="1"
  563. max="100"
  564. step="1"
  565. value="5"
  566. class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500 text-sm"
  567. placeholder="5"
  568. />
  569. <p class="text-xs text-slate-500">
  570. Homing will occur right after the clear pattern completes, before the next actual pattern begins.
  571. </p>
  572. </label>
  573. </div>
  574. </div>
  575. <div class="flex justify-end">
  576. <button
  577. id="saveHomingConfig"
  578. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  579. >
  580. <span class="material-icons text-lg">save</span>
  581. <span class="truncate">Save Configuration</span>
  582. </button>
  583. </div>
  584. </div>
  585. </section>
  586. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  587. <h2
  588. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  589. onclick="toggleSection(this)"
  590. >
  591. <span>Application Settings</span>
  592. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  593. </h2>
  594. <div class="section-content collapsed px-6 py-5 space-y-6">
  595. <label class="flex flex-col gap-1.5">
  596. <span class="text-slate-700 text-sm font-medium leading-normal"
  597. >Application Name</span
  598. >
  599. <div class="flex gap-3 items-center">
  600. <div class="relative flex-1">
  601. <input
  602. id="appNameInput"
  603. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  604. placeholder="e.g., Dune Weaver"
  605. value="Dune Weaver"
  606. />
  607. <button
  608. type="button"
  609. onclick="document.getElementById('appNameInput').value='Dune Weaver';"
  610. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  611. aria-label="Reset to default"
  612. title="Reset to default"
  613. >
  614. <span class="material-icons">restart_alt</span>
  615. </button>
  616. </div>
  617. <button
  618. id="saveAppName"
  619. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors flex-shrink-0"
  620. >
  621. <span class="material-icons text-lg">save</span>
  622. <span class="truncate">Save Name</span>
  623. </button>
  624. </div>
  625. <p class="text-xs text-slate-500 mt-2">
  626. This name will appear in the browser tab and at the top of every page.
  627. </p>
  628. </label>
  629. <!-- Custom Logo Section -->
  630. <div class="border-t border-slate-200 pt-6">
  631. <label class="flex flex-col gap-1.5">
  632. <span class="text-slate-700 text-sm font-medium leading-normal">Custom Logo & Favicon</span>
  633. <p class="text-xs text-slate-500 mb-2">
  634. Upload a custom logo to replace the default. The favicon (browser tab icon) will be automatically generated from your logo. Recommended size: 180x180 pixels. Supported formats: PNG, JPG, GIF, WebP, SVG.
  635. </p>
  636. <div class="flex gap-4 items-start">
  637. <!-- Logo Preview -->
  638. <div class="flex-shrink-0">
  639. <div id="logoPreviewContainer" class="w-16 h-16 rounded-full shadow border border-slate-200 overflow-hidden bg-slate-100 flex items-center justify-center">
  640. <img id="logoPreview" src="{% if custom_logo %}/static/custom/{{ custom_logo }}{% else %}/static/apple-touch-icon.png{% endif %}" alt="Logo Preview" class="w-full h-full object-cover"/>
  641. </div>
  642. </div>
  643. <!-- Upload Controls -->
  644. <div class="flex-1 space-y-2">
  645. <div class="flex gap-2 flex-wrap">
  646. <label class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors">
  647. <span class="material-icons text-lg">upload</span>
  648. <span>Upload Logo</span>
  649. <input type="file" id="logoFileInput" accept=".png,.jpg,.jpeg,.gif,.webp,.svg" class="hidden" />
  650. </label>
  651. <button
  652. type="button"
  653. id="resetLogoBtn"
  654. class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-10 px-4 border border-slate-300 hover:bg-slate-50 text-slate-700 text-sm font-medium leading-normal transition-colors {% if not custom_logo %}hidden{% endif %}"
  655. >
  656. <span class="material-icons text-lg">restart_alt</span>
  657. <span>Reset to Default</span>
  658. </button>
  659. </div>
  660. <p id="logoUploadStatus" class="text-xs text-slate-500"></p>
  661. </div>
  662. </div>
  663. </label>
  664. </div>
  665. </div>
  666. </section>
  667. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  668. <h2
  669. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  670. onclick="toggleSection(this)"
  671. >
  672. <span>Pattern Clearing</span>
  673. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  674. </h2>
  675. <div class="section-content collapsed px-6 py-5 space-y-6">
  676. <p class="text-sm text-slate-600">
  677. Customize the clearing behavior used when transitioning between patterns. Set custom patterns and speed to control how sand is distributed.
  678. </p>
  679. <!-- Clearing Speed Section -->
  680. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  681. <h3 class="text-slate-800 text-base font-semibold">Clearing Speed</h3>
  682. <p class="text-sm text-slate-600">
  683. Set a custom speed for clearing patterns. Leave empty to use the default pattern speed. This allows clearing patterns to run at a different speed than regular patterns.
  684. </p>
  685. <div class="flex flex-col gap-1.5">
  686. <label for="clearPatternSpeedInput" class="text-slate-700 text-sm font-medium leading-normal">
  687. Speed (steps per minute)
  688. </label>
  689. <div class="flex gap-3 items-center">
  690. <input
  691. id="clearPatternSpeedInput"
  692. type="number"
  693. min="50"
  694. max="2000"
  695. step="50"
  696. class="form-input flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  697. placeholder="Default (use pattern speed)"
  698. value=""
  699. />
  700. <button
  701. id="saveClearSpeed"
  702. class="flex items-center justify-center gap-2 min-w-[120px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  703. >
  704. <span class="material-icons text-lg">save</span>
  705. <span class="truncate">Save Speed</span>
  706. </button>
  707. </div>
  708. <div id="effectiveClearSpeed" class="text-xs text-slate-500 mt-1"></div>
  709. </div>
  710. </div>
  711. <!-- Custom Patterns Section -->
  712. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  713. <h3 class="text-slate-800 text-base font-semibold">Custom Clear Patterns</h3>
  714. <p class="text-sm text-slate-600">
  715. Choose specific patterns to use when clearing. Leave empty to use the default clearing behavior.
  716. </p>
  717. <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
  718. <div class="flex flex-col gap-1.5">
  719. <label for="customClearFromInInput" class="text-slate-700 text-sm font-medium leading-normal">Clear From Center Pattern</label>
  720. <div class="relative">
  721. <input
  722. id="customClearFromInInput"
  723. type="text"
  724. class="form-input w-full resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  725. placeholder="Type to search patterns or leave empty for default"
  726. autocomplete="off"
  727. />
  728. <button
  729. type="button"
  730. id="clearFromInClear"
  731. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hidden"
  732. aria-label="Clear selection"
  733. title="Clear selection"
  734. >
  735. <span class="material-icons text-xl">close</span>
  736. </button>
  737. <div id="clearFromInSuggestions" class="absolute z-10 w-full mt-1 bg-white border border-slate-300 rounded-lg shadow-lg max-h-60 overflow-y-auto hidden"></div>
  738. </div>
  739. <p class="text-xs text-slate-500 mt-1">
  740. Pattern to use when clearing from the center outward.
  741. </p>
  742. </div>
  743. <div class="flex flex-col gap-1.5">
  744. <label for="customClearFromOutInput" class="text-slate-700 text-sm font-medium leading-normal">Clear From Perimeter Pattern</label>
  745. <div class="relative">
  746. <input
  747. id="customClearFromOutInput"
  748. type="text"
  749. class="form-input w-full resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  750. placeholder="Type to search patterns or leave empty for default"
  751. autocomplete="off"
  752. />
  753. <button
  754. type="button"
  755. id="clearFromOutClear"
  756. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hidden"
  757. aria-label="Clear selection"
  758. title="Clear selection"
  759. >
  760. <span class="material-icons text-xl">close</span>
  761. </button>
  762. <div id="clearFromOutSuggestions" class="absolute z-10 w-full mt-1 bg-white border border-slate-300 rounded-lg shadow-lg max-h-60 overflow-y-auto hidden"></div>
  763. </div>
  764. <p class="text-xs text-slate-500 mt-1">
  765. Pattern to use when clearing from the perimeter inward.
  766. </p>
  767. </div>
  768. </div>
  769. <div class="flex justify-end">
  770. <button
  771. id="saveClearPatterns"
  772. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  773. >
  774. <span class="material-icons text-lg">save</span>
  775. <span class="truncate">Save Patterns</span>
  776. </button>
  777. </div>
  778. </div>
  779. </div>
  780. </section>
  781. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  782. <h2
  783. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  784. onclick="toggleSection(this)"
  785. >
  786. <span>LED Controller Configuration</span>
  787. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  788. </h2>
  789. <div class="section-content collapsed px-6 py-5 space-y-6">
  790. <!-- LED Provider Selection -->
  791. <div class="flex flex-col gap-2">
  792. <span class="text-slate-700 text-sm font-medium leading-normal">LED Provider</span>
  793. <div class="flex gap-3">
  794. <label class="flex items-center gap-2 cursor-pointer">
  795. <input type="radio" name="ledProvider" value="none" id="ledProviderNone" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  796. <span class="text-sm text-slate-700">None</span>
  797. </label>
  798. <label class="flex items-center gap-2 cursor-pointer">
  799. <input type="radio" name="ledProvider" value="wled" id="ledProviderWled" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  800. <span class="text-sm text-slate-700">WLED</span>
  801. </label>
  802. <label class="flex items-center gap-2 cursor-pointer">
  803. <input type="radio" name="ledProvider" value="dw_leds" id="ledProviderDwLeds" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  804. <span class="text-sm text-slate-700">DW LEDs (Local GPIO)</span>
  805. </label>
  806. </div>
  807. <p class="text-xs text-slate-500">
  808. Select your LED control system (settings are mutually exclusive)
  809. </p>
  810. </div>
  811. <!-- WLED Configuration (shown when WLED is selected) -->
  812. <div id="wledConfig" class="flex flex-col gap-4 hidden">
  813. <label class="flex flex-col gap-1.5">
  814. <span class="text-slate-700 text-sm font-medium leading-normal">WLED IP Address</span>
  815. <div class="relative flex-1">
  816. <input
  817. id="wledIpInput"
  818. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  819. placeholder="e.g., 192.168.1.100"
  820. value=""
  821. />
  822. <button
  823. type="button"
  824. onclick="document.getElementById('wledIpInput').value='';"
  825. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  826. aria-label="Clear WLED IP"
  827. >
  828. <span class="material-icons">close</span>
  829. </button>
  830. </div>
  831. <p class="text-xs text-slate-500">
  832. Enter the IP address of your WLED controller
  833. </p>
  834. </label>
  835. </div>
  836. <!-- DW LEDs Configuration (shown when DW LEDs is selected) -->
  837. <div id="dwLedsConfig" class="flex flex-col gap-4 hidden">
  838. <div class="bg-blue-50 border border-blue-200 rounded-lg p-3">
  839. <div class="flex items-start gap-2">
  840. <span class="material-icons text-blue-600 text-base">info</span>
  841. <div class="text-xs text-blue-700">
  842. <p class="font-medium text-blue-800">Supported LED Strips</p>
  843. <p class="mt-1"><strong>RGB (3-channel):</strong> WS2811, WS2812, WS2812B, WS2813, WS2815 and other WS281x strips.</p>
  844. <p class="mt-1"><strong>RGBW (4-channel):</strong> SK6812, SK6812W and other RGBW strips with dedicated white channel.</p>
  845. </div>
  846. </div>
  847. </div>
  848. <label class="flex flex-col gap-1.5">
  849. <span class="text-slate-700 text-sm font-medium leading-normal">Number of LEDs</span>
  850. <input
  851. id="dwLedNumLeds"
  852. type="number"
  853. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  854. placeholder="60"
  855. value="60"
  856. min="1"
  857. max="1000"
  858. />
  859. <p class="text-xs text-slate-500">
  860. Total number of LEDs in your WS281x strip
  861. </p>
  862. </label>
  863. <label class="flex flex-col gap-1.5">
  864. <span class="text-slate-700 text-sm font-medium leading-normal">GPIO Pin</span>
  865. <select
  866. id="dwLedGpioPin"
  867. class="form-select flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 px-4 text-base font-normal leading-normal transition-colors"
  868. >
  869. <option value="12">GPIO 12 (PWM0)</option>
  870. <option value="13">GPIO 13 (PWM1)</option>
  871. <option value="18">GPIO 18 (PWM0)</option>
  872. <option value="19">GPIO 19 (PWM1)</option>
  873. </select>
  874. <p class="text-xs text-slate-500">
  875. Select a PWM-capable GPIO pin for WS281x timing
  876. </p>
  877. </label>
  878. <label class="flex flex-col gap-1.5">
  879. <span class="text-slate-700 text-sm font-medium leading-normal">Pixel Color Order</span>
  880. <select
  881. id="dwLedPixelOrder"
  882. class="form-select flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 px-4 text-base font-normal leading-normal transition-colors"
  883. >
  884. <optgroup label="RGB Strips (3-channel)">
  885. <option value="GRB" selected>GRB - WS2812/WS2812B (most common)</option>
  886. <option value="RGB">RGB - WS2815/WS2811 and some variants</option>
  887. <option value="BGR">BGR - Some WS2811 variants</option>
  888. <option value="RBG">RBG - Rare variant</option>
  889. <option value="GBR">GBR - Rare variant</option>
  890. <option value="BRG">BRG - Rare variant</option>
  891. </optgroup>
  892. <optgroup label="RGBW Strips (4-channel)">
  893. <option value="GRBW">GRBW - SK6812 RGBW (most common)</option>
  894. <option value="RGBW">RGBW - SK6812 RGBW variant</option>
  895. </optgroup>
  896. </select>
  897. <p class="text-xs text-slate-500">
  898. Most WS2812B strips use GRB. SK6812 RGBW strips typically use GRBW. If colors appear wrong, try different orders.
  899. </p>
  900. </label>
  901. </div>
  902. <!-- Save Button -->
  903. <button
  904. id="saveLedConfig"
  905. class="flex items-center justify-center gap-2 w-full sm:w-auto cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  906. >
  907. <span class="material-icons text-lg">save</span>
  908. <span class="truncate">Save LED Configuration</span>
  909. </button>
  910. </div>
  911. </section>
  912. <!-- MQTT Configuration Section -->
  913. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  914. <h2
  915. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  916. onclick="toggleSection(this)"
  917. >
  918. <span>Home Assistant Integration</span>
  919. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  920. </h2>
  921. <div class="section-content collapsed px-6 py-5 space-y-6">
  922. <!-- MQTT Enable Toggle -->
  923. <div class="flex items-center justify-between">
  924. <div class="flex-1">
  925. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable MQTT</h3>
  926. <p class="text-xs text-slate-500 mt-1">
  927. Connect to an MQTT broker for Home Assistant integration and remote control.
  928. </p>
  929. </div>
  930. <label class="switch">
  931. <input type="checkbox" id="mqttEnableToggle">
  932. <span class="slider round"></span>
  933. </label>
  934. </div>
  935. <!-- Connection Status -->
  936. <div id="mqttStatusBanner" class="hidden">
  937. <div id="mqttConnectedBanner" class="bg-green-50 border border-green-200 rounded-lg p-3 hidden">
  938. <div class="flex items-center gap-2">
  939. <span class="material-icons text-green-600 text-base">check_circle</span>
  940. <span class="text-sm text-green-700 font-medium">Connected to MQTT broker</span>
  941. </div>
  942. </div>
  943. <div id="mqttDisconnectedBanner" class="bg-amber-50 border border-amber-200 rounded-lg p-3 hidden">
  944. <div class="flex items-center gap-2">
  945. <span class="material-icons text-amber-600 text-base">warning</span>
  946. <span class="text-sm text-amber-700 font-medium">MQTT is enabled but not connected. Check your settings or restart the application.</span>
  947. </div>
  948. </div>
  949. </div>
  950. <!-- MQTT Settings (shown when enabled) -->
  951. <div id="mqttSettings" class="space-y-4" style="display: none;">
  952. <!-- Broker Settings -->
  953. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  954. <label class="flex flex-col gap-1.5">
  955. <span class="text-slate-700 text-sm font-medium leading-normal">Broker Address <span class="text-red-500">*</span></span>
  956. <input
  957. id="mqttBrokerInput"
  958. type="text"
  959. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  960. placeholder="e.g., 192.168.1.100 or mqtt.local"
  961. />
  962. <p class="text-xs text-slate-500">IP address or hostname of your MQTT broker</p>
  963. </label>
  964. <label class="flex flex-col gap-1.5">
  965. <span class="text-slate-700 text-sm font-medium leading-normal">Port</span>
  966. <input
  967. id="mqttPortInput"
  968. type="number"
  969. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  970. placeholder="1883"
  971. value="1883"
  972. />
  973. <p class="text-xs text-slate-500">Default: 1883 (or 8883 for TLS)</p>
  974. </label>
  975. </div>
  976. <!-- Authentication -->
  977. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  978. <label class="flex flex-col gap-1.5">
  979. <span class="text-slate-700 text-sm font-medium leading-normal">Username</span>
  980. <input
  981. id="mqttUsernameInput"
  982. type="text"
  983. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  984. placeholder="Optional"
  985. />
  986. <p class="text-xs text-slate-500">Leave empty if no authentication required</p>
  987. </label>
  988. <label class="flex flex-col gap-1.5">
  989. <span class="text-slate-700 text-sm font-medium leading-normal">Password</span>
  990. <div class="relative">
  991. <input
  992. id="mqttPasswordInput"
  993. type="password"
  994. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  995. placeholder="Optional"
  996. />
  997. <button
  998. type="button"
  999. onclick="togglePasswordVisibility('mqttPasswordInput', this)"
  1000. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  1001. aria-label="Toggle password visibility"
  1002. >
  1003. <span class="material-icons text-xl">visibility_off</span>
  1004. </button>
  1005. </div>
  1006. <p class="text-xs text-slate-500">Leave empty if no authentication required</p>
  1007. </label>
  1008. </div>
  1009. <!-- Home Assistant Discovery Settings -->
  1010. <div class="border-t border-slate-200 pt-4">
  1011. <h4 class="text-slate-700 text-sm font-medium mb-3">Home Assistant Discovery</h4>
  1012. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  1013. <label class="flex flex-col gap-1.5">
  1014. <span class="text-slate-700 text-sm font-medium leading-normal">Device Name</span>
  1015. <input
  1016. id="mqttDeviceNameInput"
  1017. type="text"
  1018. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  1019. placeholder="Dune Weaver"
  1020. value="Dune Weaver"
  1021. />
  1022. <p class="text-xs text-slate-500">Display name in Home Assistant</p>
  1023. </label>
  1024. <label class="flex flex-col gap-1.5">
  1025. <span class="text-slate-700 text-sm font-medium leading-normal">Device ID</span>
  1026. <input
  1027. id="mqttDeviceIdInput"
  1028. type="text"
  1029. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  1030. placeholder="dune_weaver"
  1031. value="dune_weaver"
  1032. />
  1033. <p class="text-xs text-slate-500">Must be unique per table (no spaces). Used for MQTT topics.</p>
  1034. </label>
  1035. </div>
  1036. </div>
  1037. <!-- Client ID and Discovery Prefix -->
  1038. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  1039. <label class="flex flex-col gap-1.5">
  1040. <span class="text-slate-700 text-sm font-medium leading-normal">Client ID</span>
  1041. <input
  1042. id="mqttClientIdInput"
  1043. type="text"
  1044. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  1045. placeholder="dune_weaver"
  1046. value="dune_weaver"
  1047. />
  1048. <p class="text-xs text-slate-500">Must be unique if running multiple tables on the same broker</p>
  1049. </label>
  1050. <label class="flex flex-col gap-1.5">
  1051. <span class="text-slate-700 text-sm font-medium leading-normal">Discovery Prefix</span>
  1052. <input
  1053. id="mqttDiscoveryPrefixInput"
  1054. type="text"
  1055. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  1056. placeholder="homeassistant"
  1057. value="homeassistant"
  1058. />
  1059. <p class="text-xs text-slate-500">Home Assistant discovery topic prefix</p>
  1060. </label>
  1061. </div>
  1062. <!-- Test Connection Button -->
  1063. <div class="flex flex-wrap gap-3 pt-2">
  1064. <button
  1065. id="testMqttConnection"
  1066. class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-10 px-4 bg-slate-100 hover:bg-slate-200 text-slate-700 text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  1067. >
  1068. <span class="material-icons text-lg">wifi_tethering</span>
  1069. <span class="truncate">Test Connection</span>
  1070. </button>
  1071. <span id="mqttTestResult" class="flex items-center text-sm"></span>
  1072. </div>
  1073. </div>
  1074. <!-- Save Button -->
  1075. <button
  1076. id="saveMqttConfig"
  1077. class="flex items-center justify-center gap-2 w-full sm:w-auto cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  1078. >
  1079. <span class="material-icons text-lg">save</span>
  1080. <span class="truncate">Save MQTT Configuration</span>
  1081. </button>
  1082. <!-- Restart Notice -->
  1083. <div id="mqttRestartNotice" class="bg-blue-50 border border-blue-200 rounded-lg p-3 hidden">
  1084. <div class="flex items-start gap-2">
  1085. <span class="material-icons text-blue-600 text-base">info</span>
  1086. <div class="text-xs text-blue-700">
  1087. <p class="font-medium text-blue-800">Restart Required</p>
  1088. <p class="mt-1">MQTT configuration changes require a restart to take effect. Use the restart button in the header to apply changes.</p>
  1089. </div>
  1090. </div>
  1091. </div>
  1092. </div>
  1093. </section>
  1094. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  1095. <h2
  1096. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  1097. onclick="toggleSection(this)"
  1098. >
  1099. <span>Auto-play on Boot</span>
  1100. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  1101. </h2>
  1102. <div class="section-content collapsed px-6 py-5 space-y-6">
  1103. <div class="flex items-center justify-between">
  1104. <div class="flex-1">
  1105. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable Auto-play on Boot</h3>
  1106. <p class="text-xs text-slate-500 mt-1">
  1107. Automatically start playing a selected playlist when the system boots up.
  1108. </p>
  1109. </div>
  1110. <label class="switch">
  1111. <input type="checkbox" id="auto_playModeToggle">
  1112. <span class="slider round"></span>
  1113. </label>
  1114. </div>
  1115. <div id="auto_playSettings" class="space-y-4" style="display: none;">
  1116. <label class="flex flex-col gap-1.5">
  1117. <span class="text-slate-700 text-sm font-medium leading-normal">Startup Playlist</span>
  1118. <select
  1119. id="auto_playPlaylistSelect"
  1120. class="form-select flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  1121. >
  1122. <option value="">Select a playlist...</option>
  1123. </select>
  1124. <p class="text-xs text-slate-500 mt-1">
  1125. Choose which playlist to automatically play when the system starts.
  1126. </p>
  1127. </label>
  1128. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  1129. <label class="flex flex-col gap-1.5">
  1130. <span class="text-slate-700 text-sm font-medium leading-normal">Run Mode</span>
  1131. <select
  1132. id="auto_playRunModeSelect"
  1133. class="form-select resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  1134. >
  1135. <option value="single">Single (play once)</option>
  1136. <option value="loop">Loop (repeat forever)</option>
  1137. </select>
  1138. <p class="text-xs text-slate-500 mt-1">
  1139. How to run the playlist when it finishes.
  1140. </p>
  1141. </label>
  1142. <label class="flex flex-col gap-1.5">
  1143. <span class="text-slate-700 text-sm font-medium leading-normal">Pause Between Patterns (seconds)</span>
  1144. <input
  1145. id="auto_playPauseTimeInput"
  1146. type="number"
  1147. min="0"
  1148. step="0.5"
  1149. class="form-input resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  1150. placeholder="5.0"
  1151. />
  1152. <p class="text-xs text-slate-500 mt-1">
  1153. Time to wait between each pattern (0 or more seconds).
  1154. </p>
  1155. </label>
  1156. </div>
  1157. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  1158. <label class="flex flex-col gap-1.5">
  1159. <span class="text-slate-700 text-sm font-medium leading-normal">Clear Pattern</span>
  1160. <select
  1161. id="auto_playClearPatternSelect"
  1162. class="form-select resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  1163. >
  1164. <option value="none">None</option>
  1165. <option value="adaptive">Adaptive</option>
  1166. <option value="clear_from_in">Clear From Center</option>
  1167. <option value="clear_from_out">Clear From Perimeter</option>
  1168. <option value="clear_sideway">Clear Sideway</option>
  1169. <option value="random">Random</option>
  1170. </select>
  1171. <p class="text-xs text-slate-500 mt-1">
  1172. Pattern to run before each main pattern.
  1173. </p>
  1174. </label>
  1175. <div class="flex items-center justify-between">
  1176. <div class="flex-1">
  1177. <h4 class="text-slate-700 text-sm font-medium leading-normal">Shuffle Playlist</h4>
  1178. <p class="text-xs text-slate-500 mt-1">
  1179. Randomize the order of patterns in the playlist.
  1180. </p>
  1181. </div>
  1182. <label class="switch">
  1183. <input type="checkbox" id="auto_playShuffleToggle">
  1184. <span class="slider round"></span>
  1185. </label>
  1186. </div>
  1187. </div>
  1188. <div class="flex justify-end">
  1189. <button
  1190. id="saveAutoPlaySettings"
  1191. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  1192. >
  1193. <span class="material-icons text-lg">save</span>
  1194. <span class="truncate">Save Auto-play</span>
  1195. </button>
  1196. </div>
  1197. </div>
  1198. </div>
  1199. </section>
  1200. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  1201. <h2
  1202. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  1203. onclick="toggleSection(this)"
  1204. >
  1205. <span>Still Sands</span>
  1206. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  1207. </h2>
  1208. <div class="section-content collapsed px-6 py-5 space-y-6">
  1209. <div class="flex items-center justify-between">
  1210. <div class="flex-1">
  1211. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable Still Sands</h3>
  1212. <p class="text-xs text-slate-500 mt-1">
  1213. Automatically bring the sands to rest during specified time periods.
  1214. </p>
  1215. </div>
  1216. <label class="switch">
  1217. <input type="checkbox" id="scheduledPauseToggle">
  1218. <span class="slider round"></span>
  1219. </label>
  1220. </div>
  1221. <div id="scheduledPauseSettings" class="space-y-4" style="display: none;">
  1222. <!-- Finish Current Pattern Option -->
  1223. <div class="bg-sky-50 rounded-lg p-4 border border-sky-200">
  1224. <div class="flex items-center justify-between">
  1225. <div class="flex-1">
  1226. <h4 class="text-slate-800 text-sm font-medium flex items-center gap-2">
  1227. <span class="material-icons text-slate-800 dark:text-slate-200 text-base">hourglass_bottom</span>
  1228. Finish Current Pattern
  1229. </h4>
  1230. <p class="text-xs text-slate-600 mt-1">
  1231. Let the current pattern complete before entering still mode
  1232. </p>
  1233. </div>
  1234. <label class="switch">
  1235. <input type="checkbox" id="stillSandsFinishPattern">
  1236. <span class="slider round"></span>
  1237. </label>
  1238. </div>
  1239. </div>
  1240. <!-- WLED Control Option -->
  1241. <div class="bg-sky-50 rounded-lg p-4 border border-sky-200">
  1242. <div class="flex items-center justify-between">
  1243. <div class="flex-1">
  1244. <h4 class="text-slate-800 text-sm font-medium flex items-center gap-2">
  1245. <span class="material-icons text-slate-800 dark:text-slate-200 text-base">lightbulb</span>
  1246. Control WLED Lights
  1247. </h4>
  1248. <p class="text-xs text-slate-600 mt-1">
  1249. Turn off WLED lights during still periods for complete rest
  1250. </p>
  1251. </div>
  1252. <label class="switch">
  1253. <input type="checkbox" id="stillSandsWledControl">
  1254. <span class="slider round"></span>
  1255. </label>
  1256. </div>
  1257. </div>
  1258. <!-- Timezone Selection -->
  1259. <div class="bg-sky-50 rounded-lg p-4 border border-sky-200">
  1260. <div class="flex items-center justify-between">
  1261. <div class="flex-1">
  1262. <h4 class="text-slate-800 text-sm font-medium flex items-center gap-2">
  1263. <span class="material-icons text-slate-800 dark:text-slate-200 text-base">schedule</span>
  1264. Timezone
  1265. </h4>
  1266. <p class="text-xs text-slate-600 mt-1">
  1267. Select a timezone for still periods (defaults to system timezone)
  1268. </p>
  1269. </div>
  1270. <select id="stillSandsTimezone" class="h-10 px-3 rounded-lg border border-slate-300 bg-white text-slate-800 text-sm min-w-[200px]">
  1271. <option value="">System Default</option>
  1272. <optgroup label="Americas">
  1273. <option value="America/New_York">Eastern Time (New York)</option>
  1274. <option value="America/Chicago">Central Time (Chicago)</option>
  1275. <option value="America/Denver">Mountain Time (Denver)</option>
  1276. <option value="America/Los_Angeles">Pacific Time (Los Angeles)</option>
  1277. <option value="America/Anchorage">Alaska (Anchorage)</option>
  1278. <option value="Pacific/Honolulu">Hawaii (Honolulu)</option>
  1279. <option value="America/Toronto">Toronto</option>
  1280. <option value="America/Vancouver">Vancouver</option>
  1281. <option value="America/Mexico_City">Mexico City</option>
  1282. <option value="America/Sao_Paulo">São Paulo</option>
  1283. <option value="America/Buenos_Aires">Buenos Aires</option>
  1284. </optgroup>
  1285. <optgroup label="Europe">
  1286. <option value="Europe/London">London</option>
  1287. <option value="Europe/Paris">Paris</option>
  1288. <option value="Europe/Berlin">Berlin</option>
  1289. <option value="Europe/Amsterdam">Amsterdam</option>
  1290. <option value="Europe/Rome">Rome</option>
  1291. <option value="Europe/Madrid">Madrid</option>
  1292. <option value="Europe/Zurich">Zurich</option>
  1293. <option value="Europe/Stockholm">Stockholm</option>
  1294. <option value="Europe/Moscow">Moscow</option>
  1295. </optgroup>
  1296. <optgroup label="Asia & Pacific">
  1297. <option value="Asia/Tokyo">Tokyo</option>
  1298. <option value="Asia/Shanghai">Shanghai</option>
  1299. <option value="Asia/Hong_Kong">Hong Kong</option>
  1300. <option value="Asia/Singapore">Singapore</option>
  1301. <option value="Asia/Seoul">Seoul</option>
  1302. <option value="Asia/Dubai">Dubai</option>
  1303. <option value="Asia/Kolkata">India (Kolkata)</option>
  1304. <option value="Asia/Bangkok">Bangkok</option>
  1305. <option value="Australia/Sydney">Sydney</option>
  1306. <option value="Australia/Melbourne">Melbourne</option>
  1307. <option value="Australia/Perth">Perth</option>
  1308. <option value="Pacific/Auckland">Auckland</option>
  1309. </optgroup>
  1310. <optgroup label="Africa">
  1311. <option value="Africa/Cairo">Cairo</option>
  1312. <option value="Africa/Johannesburg">Johannesburg</option>
  1313. <option value="Africa/Lagos">Lagos</option>
  1314. </optgroup>
  1315. <optgroup label="GMT Offsets">
  1316. <option value="Etc/GMT+12">GMT-12</option>
  1317. <option value="Etc/GMT+11">GMT-11</option>
  1318. <option value="Etc/GMT+10">GMT-10</option>
  1319. <option value="Etc/GMT+9">GMT-9</option>
  1320. <option value="Etc/GMT+8">GMT-8</option>
  1321. <option value="Etc/GMT+7">GMT-7</option>
  1322. <option value="Etc/GMT+6">GMT-6</option>
  1323. <option value="Etc/GMT+5">GMT-5</option>
  1324. <option value="Etc/GMT+4">GMT-4</option>
  1325. <option value="Etc/GMT+3">GMT-3</option>
  1326. <option value="Etc/GMT+2">GMT-2</option>
  1327. <option value="Etc/GMT+1">GMT-1</option>
  1328. <option value="Etc/GMT">GMT / UTC</option>
  1329. <option value="Etc/GMT-1">GMT+1</option>
  1330. <option value="Etc/GMT-2">GMT+2</option>
  1331. <option value="Etc/GMT-3">GMT+3</option>
  1332. <option value="Etc/GMT-4">GMT+4</option>
  1333. <option value="Etc/GMT-5">GMT+5</option>
  1334. <option value="Etc/GMT-6">GMT+6</option>
  1335. <option value="Etc/GMT-7">GMT+7</option>
  1336. <option value="Etc/GMT-8">GMT+8</option>
  1337. <option value="Etc/GMT-9">GMT+9</option>
  1338. <option value="Etc/GMT-10">GMT+10</option>
  1339. <option value="Etc/GMT-11">GMT+11</option>
  1340. <option value="Etc/GMT-12">GMT+12</option>
  1341. <option value="Etc/GMT-13">GMT+13</option>
  1342. <option value="Etc/GMT-14">GMT+14</option>
  1343. </optgroup>
  1344. </select>
  1345. </div>
  1346. </div>
  1347. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  1348. <div class="flex items-center justify-between">
  1349. <h4 class="text-slate-800 text-base font-semibold">Still Periods</h4>
  1350. <button
  1351. id="addTimeSlotButton"
  1352. class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-9 px-3 bg-sky-600 hover:bg-sky-700 text-white text-xs font-medium leading-normal tracking-[0.015em] transition-colors"
  1353. >
  1354. <span class="material-icons text-base">add</span>
  1355. <span>Add Still Period</span>
  1356. </button>
  1357. </div>
  1358. <p class="text-sm text-slate-600">
  1359. Define time periods when the sands should rest in stillness. Patterns will resume automatically when still periods end.
  1360. </p>
  1361. <div id="timeSlotsContainer" class="space-y-3">
  1362. <!-- Time slots will be dynamically added here -->
  1363. </div>
  1364. <div class="text-xs text-slate-500 bg-blue-50 border border-blue-200 rounded-lg p-3">
  1365. <div class="flex items-start gap-2">
  1366. <span class="material-icons text-blue-600 text-base">info</span>
  1367. <div>
  1368. <p class="font-medium text-blue-800">Important Notes:</p>
  1369. <ul class="mt-1 space-y-1 text-blue-700">
  1370. <li>• Times are based on the selected timezone (or system default if not set)</li>
  1371. <li>• By default, patterns pause immediately when entering a still period</li>
  1372. <li>• Enable "Finish Current Pattern" to let patterns complete first</li>
  1373. <li>• Patterns will resume automatically when exiting a still period</li>
  1374. <li>• Still periods that span midnight (e.g., 22:00 to 06:00) are supported</li>
  1375. </ul>
  1376. </div>
  1377. </div>
  1378. </div>
  1379. </div>
  1380. <div class="flex justify-end">
  1381. <button
  1382. id="savePauseSettings"
  1383. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  1384. >
  1385. <span class="material-icons text-lg">save</span>
  1386. <span class="truncate">Save Still Sands</span>
  1387. </button>
  1388. </div>
  1389. </div>
  1390. </div>
  1391. </section>
  1392. <section id="software-version-section" class="bg-white rounded-xl shadow-sm overflow-hidden">
  1393. <h2
  1394. class="section-header collapsed text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  1395. onclick="toggleSection(this)"
  1396. >
  1397. <span>Software Version</span>
  1398. <span class="material-icons section-toggle-icon text-slate-400">expand_more</span>
  1399. </h2>
  1400. <div class="section-content collapsed">
  1401. <div class="flex items-center gap-4 px-6 py-5">
  1402. <div
  1403. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  1404. >
  1405. <span class="material-icons text-3xl">terminal</span>
  1406. </div>
  1407. <div class="flex-1">
  1408. <p class="text-slate-800 text-base font-medium leading-normal">
  1409. Current Version
  1410. </p>
  1411. <p id="currentVersionText" class="text-slate-500 text-sm font-normal leading-normal">Loading...</p>
  1412. </div>
  1413. </div>
  1414. <div class="flex items-center gap-4 px-6 py-5">
  1415. <div
  1416. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  1417. >
  1418. <span class="material-icons text-3xl">system_update</span>
  1419. </div>
  1420. <div class="flex-1">
  1421. <p class="text-slate-800 text-base font-medium leading-normal">
  1422. Latest Version
  1423. </p>
  1424. <p id="latestVersionText" class="text-slate-500 text-sm font-normal leading-normal">Checking...</p>
  1425. </div>
  1426. <button
  1427. id="updateSoftware"
  1428. class="flex items-center justify-center gap-1.5 min-w-[84px] cursor-pointer rounded-lg h-9 px-3 bg-gray-400 text-white text-xs font-medium leading-normal tracking-[0.015em] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
  1429. disabled
  1430. >
  1431. <span id="updateIcon" class="material-icons text-base">download</span>
  1432. <span id="updateText" class="truncate">Update</span>
  1433. </button>
  1434. </div>
  1435. <div class="flex items-center gap-4 px-6 py-5 border-t border-slate-200">
  1436. <div
  1437. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  1438. >
  1439. <span class="material-icons text-3xl">article</span>
  1440. </div>
  1441. <div class="flex-1">
  1442. <p class="text-slate-800 text-base font-medium leading-normal">
  1443. Application Logs
  1444. </p>
  1445. <p class="text-slate-500 text-sm font-normal leading-normal">View real-time application logs</p>
  1446. </div>
  1447. <button
  1448. id="openLogsBtn"
  1449. onclick="openLogsModal()"
  1450. class="flex items-center justify-center gap-1.5 min-w-[84px] cursor-pointer rounded-lg h-9 px-3 bg-blue-600 hover:bg-blue-700 text-white text-xs font-medium leading-normal tracking-[0.015em] transition-colors"
  1451. >
  1452. <span class="material-icons text-base">terminal</span>
  1453. <span class="truncate">View Logs</span>
  1454. </button>
  1455. </div>
  1456. </div>
  1457. </section>
  1458. </div>
  1459. {% endblock %} {% block scripts %}
  1460. <!-- Logs Modal - placed in scripts block to be outside main content container -->
  1461. <div id="logsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden p-4">
  1462. <div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-5xl max-h-[90vh] flex flex-col">
  1463. <!-- Modal Header -->
  1464. <div class="flex items-center justify-between px-6 py-4 border-b border-slate-200 dark:border-gray-600">
  1465. <div class="flex items-center gap-3">
  1466. <span class="material-icons text-2xl text-slate-600 dark:text-gray-300">article</span>
  1467. <h3 class="text-lg font-semibold text-slate-800 dark:text-gray-100">Application Logs</h3>
  1468. <span id="logsConnectionStatus" class="text-xs px-2 py-1 rounded-full bg-gray-200 dark:bg-gray-600 text-gray-600 dark:text-gray-300">Connecting...</span>
  1469. </div>
  1470. <div class="flex items-center gap-2">
  1471. <select id="logLevelFilter" onchange="filterLogs()" class="form-select text-sm rounded-lg border-slate-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 h-8 px-2">
  1472. <option value="">All Levels</option>
  1473. <option value="DEBUG">Debug</option>
  1474. <option value="INFO">Info</option>
  1475. <option value="WARNING">Warning</option>
  1476. <option value="ERROR">Error</option>
  1477. <option value="CRITICAL">Critical</option>
  1478. </select>
  1479. <button
  1480. onclick="clearLogs()"
  1481. class="flex items-center justify-center gap-1 rounded-lg h-8 px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-500 text-gray-700 dark:text-gray-200 text-xs font-medium transition-colors"
  1482. >
  1483. <span class="material-icons text-sm">delete_sweep</span>
  1484. Clear
  1485. </button>
  1486. <label class="flex items-center gap-1.5 text-sm text-slate-600 dark:text-gray-300">
  1487. <input type="checkbox" id="logsAutoScroll" checked class="rounded border-slate-300 dark:border-gray-600">
  1488. Auto-scroll
  1489. </label>
  1490. <button
  1491. onclick="closeLogsModal()"
  1492. class="flex items-center justify-center rounded-lg size-8 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-500 dark:text-gray-400 transition-colors"
  1493. >
  1494. <span class="material-icons">close</span>
  1495. </button>
  1496. </div>
  1497. </div>
  1498. <!-- Modal Body -->
  1499. <div id="logsContainer" class="flex-1 overflow-y-auto p-4 font-mono text-xs bg-slate-50 dark:bg-gray-900">
  1500. <div id="logsContent" class="space-y-1">
  1501. <!-- Log entries will be inserted here -->
  1502. </div>
  1503. </div>
  1504. <!-- Modal Footer -->
  1505. <div class="px-6 py-3 border-t border-slate-200 dark:border-gray-600 flex items-center justify-between text-xs text-slate-500 dark:text-gray-400">
  1506. <span id="logsCount">0 entries</span>
  1507. <span id="logsInfo">Showing last 500 log entries</span>
  1508. </div>
  1509. </div>
  1510. </div>
  1511. <script src="/static/js/settings.js"></script>
  1512. {% endblock %}