settings.html 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  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: #404040;
  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. /* Time slot specific styles */
  189. .time-slot-item {
  190. background-color: #f8fafc;
  191. border: 1px solid #e2e8f0;
  192. border-radius: 8px;
  193. padding: 16px;
  194. transition: all 0.15s;
  195. }
  196. .dark .time-slot-item {
  197. background-color: #1e293b;
  198. border-color: #475569;
  199. }
  200. .time-slot-item:hover {
  201. border-color: #cbd5e1;
  202. }
  203. .dark .time-slot-item:hover {
  204. border-color: #64748b;
  205. }
  206. /* Info box dark mode - grey theme */
  207. .dark .bg-blue-50 {
  208. background-color: #1f1f1f;
  209. }
  210. .dark .border-blue-200 {
  211. border-color: #404040;
  212. }
  213. .dark .text-blue-600 {
  214. color: #e2e8f0;
  215. }
  216. .dark .text-blue-800 {
  217. color: #f1f5f9;
  218. }
  219. .dark .text-blue-700 {
  220. color: #e2e8f0;
  221. }
  222. /* Amber box dark mode - grey theme */
  223. .dark .bg-amber-50 {
  224. background-color: #1f1f1f;
  225. }
  226. .dark .border-amber-200 {
  227. border-color: #404040;
  228. }
  229. .dark .text-amber-600 {
  230. color: #f1f5f9;
  231. }
  232. {% endblock %}
  233. {% block content %}
  234. <div class="layout-content-container flex flex-col w-full max-w-4xl gap-8 pt-2 pb-[75px]">
  235. <div
  236. class="flex flex-wrap justify-between items-center p-4 bg-white rounded-xl shadow-sm mt-2 sm:mt-8"
  237. >
  238. <h1
  239. class="text-slate-900 tracking-tight text-2xl sm:text-3xl font-bold leading-tight"
  240. >
  241. Settings
  242. </h1>
  243. </div>
  244. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  245. <h2
  246. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  247. >
  248. Device Connection
  249. </h2>
  250. <div class="divide-y divide-slate-100">
  251. <div
  252. class="flex items-center gap-4 px-6 py-5 hover:bg-slate-50 transition-colors"
  253. >
  254. <div
  255. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  256. >
  257. <span class="material-icons text-3xl">usb_off</span>
  258. </div>
  259. <div class="flex-1">
  260. <p class="text-slate-800 text-base font-medium leading-normal">
  261. Status
  262. </p>
  263. <p
  264. id="serialStatus"
  265. class="text-red-500 text-sm font-medium leading-normal"
  266. >
  267. Disconnected
  268. </p>
  269. </div>
  270. <button
  271. id="disconnectButton"
  272. class="text-xs font-medium text-slate-600 bg-red-100 hover:bg-red-200 text-red-700 px-3 py-1.5 rounded-md transition-colors"
  273. hidden
  274. >
  275. Disconnect
  276. </button>
  277. </div>
  278. <div id="portSelectionDiv" class="px-6 py-5 space-y-4">
  279. <label class="flex flex-col gap-1.5">
  280. <span class="text-slate-700 text-sm font-medium leading-normal"
  281. >Available Serial Ports</span
  282. >
  283. <div class="flex gap-3 items-center">
  284. <select
  285. id="portSelect"
  286. 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 "
  287. >
  288. <option value="">Select a port...</option>
  289. </select>
  290. <button
  291. id="connectButton"
  292. 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"
  293. >
  294. <span class="material-icons text-lg">cable</span>
  295. <span class="truncate">Connect</span>
  296. </button>
  297. </div>
  298. <p class="text-xs text-slate-500 mt-2">
  299. Select a port and click 'Connect' to establish a connection.
  300. </p>
  301. </label>
  302. </div>
  303. <!-- Preferred Port Configuration -->
  304. <div class="px-6 py-5 border-t border-slate-100">
  305. <label class="flex flex-col gap-1.5">
  306. <span class="text-slate-700 text-sm font-medium leading-normal flex items-center gap-2">
  307. <span class="material-icons text-slate-600 text-base">star</span>
  308. Preferred Port for Auto-Connect
  309. </span>
  310. <div class="flex gap-3 items-center">
  311. <select
  312. id="preferredPortSelect"
  313. 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"
  314. >
  315. <option value="">No preference (auto-detect)</option>
  316. </select>
  317. <button
  318. id="savePreferredPort"
  319. 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"
  320. >
  321. <span class="material-icons text-lg">save</span>
  322. <span class="truncate">Save</span>
  323. </button>
  324. </div>
  325. <p class="text-xs text-slate-500 mt-2">
  326. 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.
  327. </p>
  328. <p id="currentPreferredPort" class="text-xs text-sky-600 mt-1 hidden">
  329. <span class="material-icons text-xs align-middle">check_circle</span>
  330. <span id="preferredPortDisplay"></span>
  331. </p>
  332. </label>
  333. </div>
  334. </div>
  335. </section>
  336. <!-- Homing Configuration Section -->
  337. <section id="homingSection" class="bg-white rounded-xl shadow-sm overflow-hidden">
  338. <h2
  339. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  340. >
  341. Homing Configuration
  342. </h2>
  343. <div class="px-6 py-5 space-y-6">
  344. <!-- Homing Mode Selection -->
  345. <div class="space-y-3">
  346. <label class="text-sm font-medium text-slate-700 flex items-center gap-2">
  347. <span class="material-icons text-slate-600 text-base">home</span>
  348. Homing Mode
  349. </label>
  350. <div class="space-y-3">
  351. <!-- Crash Homing Option -->
  352. <label class="flex items-start gap-3 p-3 border border-slate-300 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors">
  353. <input
  354. type="radio"
  355. name="homingMode"
  356. value="0"
  357. id="homingModeCrash"
  358. class="mt-0.5 w-4 h-4 text-sky-600 focus:ring-sky-500"
  359. />
  360. <div class="flex-1">
  361. <div class="text-sm font-medium text-slate-700">Crash Homing</div>
  362. <div class="text-xs text-slate-500 mt-1">
  363. Y axis moves until physical stop, then theta and rho set to 0 (no x0 y0 command)
  364. </div>
  365. </div>
  366. </label>
  367. <!-- Sensor Homing Option -->
  368. <label class="flex items-start gap-3 p-3 border border-slate-300 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors">
  369. <input
  370. type="radio"
  371. name="homingMode"
  372. value="1"
  373. id="homingModeSensor"
  374. class="mt-0.5 w-4 h-4 text-sky-600 focus:ring-sky-500"
  375. />
  376. <div class="flex-1">
  377. <div class="text-sm font-medium text-slate-700">Sensor Homing</div>
  378. <div class="text-xs text-slate-500 mt-1">
  379. Homes both X and Y axes using sensors
  380. </div>
  381. </div>
  382. </label>
  383. </div>
  384. </div>
  385. <!-- Compass Reference Point (Sensor mode only) -->
  386. <div id="compassOffsetContainer" class="space-y-2">
  387. <label for="angularOffsetInput" class="text-sm font-medium text-slate-700 flex items-center gap-2">
  388. <span class="material-icons text-slate-600 text-base">explore</span>
  389. Sensor offset (degrees) <span class="text-xs text-slate-400">(Sensor mode only)</span>
  390. </label>
  391. <input
  392. type="number"
  393. id="angularOffsetInput"
  394. min="0"
  395. max="360"
  396. step="0.1"
  397. value="0"
  398. 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"
  399. placeholder="0.0"
  400. />
  401. <p class="text-xs text-slate-500">
  402. 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.
  403. </p>
  404. </div>
  405. <!-- Homing Info Box -->
  406. <div id="homingInfoBox" class="text-xs text-slate-600 bg-blue-50 border border-blue-200 rounded-lg p-3">
  407. <div class="flex items-start gap-2">
  408. <span class="material-icons text-blue-600 text-base">info</span>
  409. <div id="homingInfoContent">
  410. <p class="font-medium text-blue-800">Crash Homing Mode:</p>
  411. <ul class="mt-1 space-y-1 text-blue-700">
  412. <li>• Y axis moves -22mm (or -30mm for mini) until physical stop</li>
  413. <li>• Theta set to 0, rho set to 0</li>
  414. <li>• No x0 y0 command sent</li>
  415. <li>• No hardware sensors required</li>
  416. </ul>
  417. </div>
  418. </div>
  419. </div>
  420. <div class="flex justify-end">
  421. <button
  422. id="saveHomingConfig"
  423. 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"
  424. >
  425. <span class="material-icons text-lg">save</span>
  426. <span class="truncate">Save Configuration</span>
  427. </button>
  428. </div>
  429. </div>
  430. </section>
  431. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  432. <h2
  433. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  434. >
  435. Application Settings
  436. </h2>
  437. <div class="px-6 py-5 space-y-6">
  438. <label class="flex flex-col gap-1.5">
  439. <span class="text-slate-700 text-sm font-medium leading-normal"
  440. >Application Name</span
  441. >
  442. <div class="flex gap-3 items-center">
  443. <div class="relative flex-1">
  444. <input
  445. id="appNameInput"
  446. 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"
  447. placeholder="e.g., Dune Weaver"
  448. value="Dune Weaver"
  449. />
  450. <button
  451. type="button"
  452. onclick="document.getElementById('appNameInput').value='Dune Weaver';"
  453. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  454. aria-label="Reset to default"
  455. title="Reset to default"
  456. >
  457. <span class="material-icons">restart_alt</span>
  458. </button>
  459. </div>
  460. <button
  461. id="saveAppName"
  462. 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"
  463. >
  464. <span class="material-icons text-lg">save</span>
  465. <span class="truncate">Save Name</span>
  466. </button>
  467. </div>
  468. <p class="text-xs text-slate-500 mt-2">
  469. This name will appear in the browser tab and at the top of every page.
  470. </p>
  471. </label>
  472. </div>
  473. </section>
  474. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  475. <h2
  476. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  477. >
  478. Pattern Clearing
  479. </h2>
  480. <div class="px-6 py-5 space-y-6">
  481. <p class="text-sm text-slate-600">
  482. Customize the clearing behavior used when transitioning between patterns. Set custom patterns and speed to control how sand is distributed.
  483. </p>
  484. <!-- Clearing Speed Section -->
  485. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  486. <h3 class="text-slate-800 text-base font-semibold">Clearing Speed</h3>
  487. <p class="text-sm text-slate-600">
  488. 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.
  489. </p>
  490. <div class="flex flex-col gap-1.5">
  491. <label for="clearPatternSpeedInput" class="text-slate-700 text-sm font-medium leading-normal">
  492. Speed (steps per minute)
  493. </label>
  494. <div class="flex gap-3 items-center">
  495. <input
  496. id="clearPatternSpeedInput"
  497. type="number"
  498. min="50"
  499. max="2000"
  500. step="50"
  501. 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"
  502. placeholder="Default (use pattern speed)"
  503. value=""
  504. />
  505. <button
  506. id="saveClearSpeed"
  507. 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"
  508. >
  509. <span class="material-icons text-lg">save</span>
  510. <span class="truncate">Save Speed</span>
  511. </button>
  512. </div>
  513. <div id="effectiveClearSpeed" class="text-xs text-slate-500 mt-1"></div>
  514. </div>
  515. </div>
  516. <!-- Custom Patterns Section -->
  517. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  518. <h3 class="text-slate-800 text-base font-semibold">Custom Clear Patterns</h3>
  519. <p class="text-sm text-slate-600">
  520. Choose specific patterns to use when clearing. Leave empty to use the default clearing behavior.
  521. </p>
  522. <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
  523. <div class="flex flex-col gap-1.5">
  524. <label for="customClearFromInInput" class="text-slate-700 text-sm font-medium leading-normal">Clear From Center Pattern</label>
  525. <div class="relative">
  526. <input
  527. id="customClearFromInInput"
  528. type="text"
  529. 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"
  530. placeholder="Type to search patterns or leave empty for default"
  531. autocomplete="off"
  532. />
  533. <button
  534. type="button"
  535. id="clearFromInClear"
  536. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hidden"
  537. aria-label="Clear selection"
  538. title="Clear selection"
  539. >
  540. <span class="material-icons text-xl">close</span>
  541. </button>
  542. <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>
  543. </div>
  544. <p class="text-xs text-slate-500 mt-1">
  545. Pattern to use when clearing from the center outward.
  546. </p>
  547. </div>
  548. <div class="flex flex-col gap-1.5">
  549. <label for="customClearFromOutInput" class="text-slate-700 text-sm font-medium leading-normal">Clear From Perimeter Pattern</label>
  550. <div class="relative">
  551. <input
  552. id="customClearFromOutInput"
  553. type="text"
  554. 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"
  555. placeholder="Type to search patterns or leave empty for default"
  556. autocomplete="off"
  557. />
  558. <button
  559. type="button"
  560. id="clearFromOutClear"
  561. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hidden"
  562. aria-label="Clear selection"
  563. title="Clear selection"
  564. >
  565. <span class="material-icons text-xl">close</span>
  566. </button>
  567. <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>
  568. </div>
  569. <p class="text-xs text-slate-500 mt-1">
  570. Pattern to use when clearing from the perimeter inward.
  571. </p>
  572. </div>
  573. </div>
  574. <div class="flex justify-end">
  575. <button
  576. id="saveClearPatterns"
  577. 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"
  578. >
  579. <span class="material-icons text-lg">save</span>
  580. <span class="truncate">Save Patterns</span>
  581. </button>
  582. </div>
  583. </div>
  584. </div>
  585. </section>
  586. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  587. <h2
  588. class="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. >
  590. LED Controller Configuration
  591. </h2>
  592. <div class="px-6 py-5 space-y-6">
  593. <!-- LED Provider Selection -->
  594. <div class="flex flex-col gap-2">
  595. <span class="text-slate-700 text-sm font-medium leading-normal">LED Provider</span>
  596. <div class="flex gap-3">
  597. <label class="flex items-center gap-2 cursor-pointer">
  598. <input type="radio" name="ledProvider" value="none" id="ledProviderNone" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  599. <span class="text-sm text-slate-700">None</span>
  600. </label>
  601. <label class="flex items-center gap-2 cursor-pointer">
  602. <input type="radio" name="ledProvider" value="wled" id="ledProviderWled" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  603. <span class="text-sm text-slate-700">WLED</span>
  604. </label>
  605. <label class="flex items-center gap-2 cursor-pointer">
  606. <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">
  607. <span class="text-sm text-slate-700">DW LEDs (Local GPIO)</span>
  608. </label>
  609. </div>
  610. <p class="text-xs text-slate-500">
  611. Select your LED control system (settings are mutually exclusive)
  612. </p>
  613. </div>
  614. <!-- WLED Configuration (shown when WLED is selected) -->
  615. <div id="wledConfig" class="flex flex-col gap-4 hidden">
  616. <label class="flex flex-col gap-1.5">
  617. <span class="text-slate-700 text-sm font-medium leading-normal">WLED IP Address</span>
  618. <div class="relative flex-1">
  619. <input
  620. id="wledIpInput"
  621. 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"
  622. placeholder="e.g., 192.168.1.100"
  623. value=""
  624. />
  625. <button
  626. type="button"
  627. onclick="document.getElementById('wledIpInput').value='';"
  628. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  629. aria-label="Clear WLED IP"
  630. >
  631. <span class="material-icons">close</span>
  632. </button>
  633. </div>
  634. <p class="text-xs text-slate-500">
  635. Enter the IP address of your WLED controller
  636. </p>
  637. </label>
  638. </div>
  639. <!-- DW LEDs Configuration (shown when DW LEDs is selected) -->
  640. <div id="dwLedsConfig" class="flex flex-col gap-4 hidden">
  641. <div class="bg-blue-50 border border-blue-200 rounded-lg p-3">
  642. <div class="flex items-start gap-2">
  643. <span class="material-icons text-blue-600 text-base">info</span>
  644. <div class="text-xs text-blue-700">
  645. <p class="font-medium text-blue-800">Supported LED Strips</p>
  646. <p class="mt-1"><strong>RGB (3-channel):</strong> WS2811, WS2812, WS2812B, WS2813, WS2815 and other WS281x strips.</p>
  647. <p class="mt-1"><strong>RGBW (4-channel):</strong> SK6812, SK6812W and other RGBW strips with dedicated white channel.</p>
  648. </div>
  649. </div>
  650. </div>
  651. <label class="flex flex-col gap-1.5">
  652. <span class="text-slate-700 text-sm font-medium leading-normal">Number of LEDs</span>
  653. <input
  654. id="dwLedNumLeds"
  655. type="number"
  656. 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"
  657. placeholder="60"
  658. value="60"
  659. min="1"
  660. max="1000"
  661. />
  662. <p class="text-xs text-slate-500">
  663. Total number of LEDs in your WS281x strip
  664. </p>
  665. </label>
  666. <label class="flex flex-col gap-1.5">
  667. <span class="text-slate-700 text-sm font-medium leading-normal">GPIO Pin</span>
  668. <select
  669. id="dwLedGpioPin"
  670. 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"
  671. >
  672. <option value="12">GPIO 12 (PWM0)</option>
  673. <option value="13">GPIO 13 (PWM1)</option>
  674. <option value="18">GPIO 18 (PWM0)</option>
  675. <option value="19">GPIO 19 (PWM1)</option>
  676. </select>
  677. <p class="text-xs text-slate-500">
  678. Select a PWM-capable GPIO pin for WS281x timing
  679. </p>
  680. </label>
  681. <label class="flex flex-col gap-1.5">
  682. <span class="text-slate-700 text-sm font-medium leading-normal">Pixel Color Order</span>
  683. <select
  684. id="dwLedPixelOrder"
  685. 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"
  686. >
  687. <optgroup label="RGB Strips (3-channel)">
  688. <option value="GRB" selected>GRB - WS2812/WS2812B (most common)</option>
  689. <option value="RGB">RGB - WS2815/WS2811 and some variants</option>
  690. <option value="BGR">BGR - Some WS2811 variants</option>
  691. <option value="RBG">RBG - Rare variant</option>
  692. <option value="GBR">GBR - Rare variant</option>
  693. <option value="BRG">BRG - Rare variant</option>
  694. </optgroup>
  695. <optgroup label="RGBW Strips (4-channel)">
  696. <option value="GRBW">GRBW - SK6812 RGBW (most common)</option>
  697. <option value="RGBW">RGBW - SK6812 RGBW variant</option>
  698. </optgroup>
  699. </select>
  700. <p class="text-xs text-slate-500">
  701. Most WS2812B strips use GRB. SK6812 RGBW strips typically use GRBW. If colors appear wrong, try different orders.
  702. </p>
  703. </label>
  704. </div>
  705. <!-- Save Button -->
  706. <button
  707. id="saveLedConfig"
  708. 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"
  709. >
  710. <span class="material-icons text-lg">save</span>
  711. <span class="truncate">Save LED Configuration</span>
  712. </button>
  713. </div>
  714. </section>
  715. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  716. <h2
  717. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  718. >
  719. Auto-play on Boot
  720. </h2>
  721. <div class="px-6 py-5 space-y-6">
  722. <div class="flex items-center justify-between">
  723. <div class="flex-1">
  724. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable Auto-play on Boot</h3>
  725. <p class="text-xs text-slate-500 mt-1">
  726. Automatically start playing a selected playlist when the system boots up.
  727. </p>
  728. </div>
  729. <label class="switch">
  730. <input type="checkbox" id="auto_playModeToggle">
  731. <span class="slider round"></span>
  732. </label>
  733. </div>
  734. <div id="auto_playSettings" class="space-y-4" style="display: none;">
  735. <label class="flex flex-col gap-1.5">
  736. <span class="text-slate-700 text-sm font-medium leading-normal">Startup Playlist</span>
  737. <select
  738. id="auto_playPlaylistSelect"
  739. 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"
  740. >
  741. <option value="">Select a playlist...</option>
  742. </select>
  743. <p class="text-xs text-slate-500 mt-1">
  744. Choose which playlist to automatically play when the system starts.
  745. </p>
  746. </label>
  747. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  748. <label class="flex flex-col gap-1.5">
  749. <span class="text-slate-700 text-sm font-medium leading-normal">Run Mode</span>
  750. <select
  751. id="auto_playRunModeSelect"
  752. 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"
  753. >
  754. <option value="single">Single (play once)</option>
  755. <option value="loop">Loop (repeat forever)</option>
  756. </select>
  757. <p class="text-xs text-slate-500 mt-1">
  758. How to run the playlist when it finishes.
  759. </p>
  760. </label>
  761. <label class="flex flex-col gap-1.5">
  762. <span class="text-slate-700 text-sm font-medium leading-normal">Pause Between Patterns (seconds)</span>
  763. <input
  764. id="auto_playPauseTimeInput"
  765. type="number"
  766. min="0"
  767. step="0.5"
  768. 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"
  769. placeholder="5.0"
  770. />
  771. <p class="text-xs text-slate-500 mt-1">
  772. Time to wait between each pattern (0 or more seconds).
  773. </p>
  774. </label>
  775. </div>
  776. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  777. <label class="flex flex-col gap-1.5">
  778. <span class="text-slate-700 text-sm font-medium leading-normal">Clear Pattern</span>
  779. <select
  780. id="auto_playClearPatternSelect"
  781. 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"
  782. >
  783. <option value="none">None</option>
  784. <option value="adaptive">Adaptive</option>
  785. <option value="clear_from_in">Clear From Center</option>
  786. <option value="clear_from_out">Clear From Perimeter</option>
  787. <option value="clear_sideway">Clear Sideway</option>
  788. <option value="random">Random</option>
  789. </select>
  790. <p class="text-xs text-slate-500 mt-1">
  791. Pattern to run before each main pattern.
  792. </p>
  793. </label>
  794. <div class="flex items-center justify-between">
  795. <div class="flex-1">
  796. <h4 class="text-slate-700 text-sm font-medium leading-normal">Shuffle Playlist</h4>
  797. <p class="text-xs text-slate-500 mt-1">
  798. Randomize the order of patterns in the playlist.
  799. </p>
  800. </div>
  801. <label class="switch">
  802. <input type="checkbox" id="auto_playShuffleToggle">
  803. <span class="slider round"></span>
  804. </label>
  805. </div>
  806. </div>
  807. </div>
  808. </div>
  809. </section>
  810. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  811. <h2
  812. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  813. >
  814. Still Sands
  815. </h2>
  816. <div class="px-6 py-5 space-y-6">
  817. <div class="flex items-center justify-between">
  818. <div class="flex-1">
  819. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable Still Sands</h3>
  820. <p class="text-xs text-slate-500 mt-1">
  821. Automatically bring the sands to rest during specified time periods.
  822. </p>
  823. </div>
  824. <label class="switch">
  825. <input type="checkbox" id="scheduledPauseToggle">
  826. <span class="slider round"></span>
  827. </label>
  828. </div>
  829. <div id="scheduledPauseSettings" class="space-y-4" style="display: none;">
  830. <!-- WLED Control Option -->
  831. <div class="bg-amber-50 rounded-lg p-4 border border-amber-200">
  832. <div class="flex items-center justify-between">
  833. <div class="flex-1">
  834. <h4 class="text-slate-800 text-sm font-medium flex items-center gap-2">
  835. <span class="material-icons text-amber-600 text-base">lightbulb</span>
  836. Control WLED Lights
  837. </h4>
  838. <p class="text-xs text-slate-600 mt-1">
  839. Turn off WLED lights during still periods for complete rest
  840. </p>
  841. </div>
  842. <label class="switch">
  843. <input type="checkbox" id="stillSandsWledControl">
  844. <span class="slider round"></span>
  845. </label>
  846. </div>
  847. </div>
  848. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  849. <div class="flex items-center justify-between">
  850. <h4 class="text-slate-800 text-base font-semibold">Still Periods</h4>
  851. <button
  852. id="addTimeSlotButton"
  853. 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"
  854. >
  855. <span class="material-icons text-base">add</span>
  856. <span>Add Still Period</span>
  857. </button>
  858. </div>
  859. <p class="text-sm text-slate-600">
  860. Define time periods when the sands should rest in stillness. Patterns will resume automatically when still periods end.
  861. </p>
  862. <div id="timeSlotsContainer" class="space-y-3">
  863. <!-- Time slots will be dynamically added here -->
  864. </div>
  865. <div class="text-xs text-slate-500 bg-blue-50 border border-blue-200 rounded-lg p-3">
  866. <div class="flex items-start gap-2">
  867. <span class="material-icons text-blue-600 text-base">info</span>
  868. <div>
  869. <p class="font-medium text-blue-800">Important Notes:</p>
  870. <ul class="mt-1 space-y-1 text-blue-700">
  871. <li>• Times are based on your system's local time zone</li>
  872. <li>• Currently running patterns will pause immediately when entering a still period</li>
  873. <li>• Patterns will resume automatically when exiting a still period</li>
  874. <li>• Still periods that span midnight (e.g., 22:00 to 06:00) are supported</li>
  875. </ul>
  876. </div>
  877. </div>
  878. </div>
  879. </div>
  880. <div class="flex justify-end">
  881. <button
  882. id="savePauseSettings"
  883. 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"
  884. >
  885. <span class="material-icons text-lg">save</span>
  886. <span class="truncate">Save Still Sands</span>
  887. </button>
  888. </div>
  889. </div>
  890. </div>
  891. </section>
  892. <section id="software-version-section" class="bg-white rounded-xl shadow-sm overflow-hidden">
  893. <h2
  894. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  895. >
  896. Software Version
  897. </h2>
  898. <div class="divide-y divide-slate-100">
  899. <div class="flex items-center gap-4 px-6 py-5">
  900. <div
  901. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  902. >
  903. <span class="material-icons text-3xl">terminal</span>
  904. </div>
  905. <div class="flex-1">
  906. <p class="text-slate-800 text-base font-medium leading-normal">
  907. Current Version
  908. </p>
  909. <p id="currentVersionText" class="text-slate-500 text-sm font-normal leading-normal">Loading...</p>
  910. </div>
  911. </div>
  912. <div class="flex items-center gap-4 px-6 py-5">
  913. <div
  914. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  915. >
  916. <span class="material-icons text-3xl">system_update</span>
  917. </div>
  918. <div class="flex-1">
  919. <p class="text-slate-800 text-base font-medium leading-normal">
  920. Latest Version
  921. </p>
  922. <p id="latestVersionText" class="text-slate-500 text-sm font-normal leading-normal">Checking...</p>
  923. </div>
  924. <button
  925. id="updateSoftware"
  926. 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"
  927. disabled
  928. >
  929. <span id="updateIcon" class="material-icons text-base">download</span>
  930. <span id="updateText" class="truncate">Update</span>
  931. </button>
  932. </div>
  933. </div>
  934. </section>
  935. </div>
  936. {% endblock %} {% block scripts %}
  937. <script src="/static/js/settings.js"></script>
  938. {% endblock %}