Skip to content

Commit 639179a

Browse files
committed
Board presets 🚧, Multirow edit
Firmware ======== - esp32-d0/p4/s3: remove VOLTAGE_PIN, CURRENT_PIN, LED_PINS Front end ======== - MultiInput: add noPrompts and time - ObjectArray: add searchQuery, filteredItems and use MultiInput for Rows Back end ======== - main: refactor runInAppTask, remove moduleIO.loop1s - module: add updateOriginId, refactor runInAppTask, compareRecursive: updatedItem.index[0] for arrays - moduleIO: add board presets! 🚧 - Nodes: SOC_GPIO_PIN_COUNT - 1
1 parent 81e39a8 commit 639179a

File tree

25 files changed

+16055
-15621
lines changed

25 files changed

+16055
-15621
lines changed

docs/develop/modules.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ void setupDefinition(JsonArray root) override{
6161

6262
* Implement function **onUpdate** to define what happens if data changes
6363
* struct UpdatedItem defines the update (parent property (including index in case of multiple records), name of property and value)
64-
* This runs in the httpd / webserver task. To run it in another task (application task) use runInTask1 and 2 - see [ModuleLightsControl](https://github.com/MoonModules/MoonLight/blob/main/src/MoonLight/ModuleLightsControl.h)
64+
* This runs in the httpd / webserver task. To run it in another task (application task) use runInAppTask - see [ModuleLightsControl](https://github.com/MoonModules/MoonLight/blob/main/src/MoonLight/ModuleLightsControl.h)
6565

6666
```cpp
6767
void onUpdate(UpdatedItem &updatedItem) override

firmware/esp32-d0.ini

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ build_flags = ${esp32-d0-base.build_flags}
2828
-D LOLIN_WIFI_FIX ; some boards have wifi issues if this is not defined, this sets WIFI_POWER_8_5dBm
2929
; -D LED_BUILTIN=2
3030
; -D KEY_BUILTIN=0
31-
; -D VOLTAGE_PIN=8 ; 🚧
32-
; -D CURRENT_PIN=9 ; 🚧
3331
lib_deps = ${esp32-d0-base.lib_deps}
3432
; RAM: [=== ] 26.2% (used 85980 bytes from 327680 bytes)
3533
; Flash: [======= ] 65.2% (used 2051394 bytes from 3145728 bytes)
@@ -66,8 +64,6 @@ build_flags = ${esp32-d0-base.build_flags}
6664
-mfix-esp32-psram-cache-issue
6765
; -D LED_BUILTIN=2
6866
; -D USE_M5UNIFIED=1 ;for MoonManEffect (but low on heap)
69-
-D VOLTAGE_PIN=8 ; 🚧
70-
-D CURRENT_PIN=9 ; 🚧
7167
lib_deps = ${esp32-d0-base.lib_deps}
7268
${livescripts.lib_deps}
7369
; m5stack/M5Unified ;for MoonManEffect (but low on heap)

firmware/esp32-p4.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ upload_speed = 921600
2828
build_flags = ${esp32-p4-base.build_flags}
2929
-Wl,-wrap,esp_dma_capable_malloc ;; makes SDIO for ESP-Hosted use PSRAM if available.
3030
; -D FT_ETHERNET=1 ; 🚧 not implemented yet
31-
-D LED_PINS=\"2,3,4,5,6,20,21,22,23,26,27,32,33,36,47,48\" ; 🚧 Troy default showed in IO but not used in PIN allocations yet: set them in layout
3231

3332
lib_deps = ${esp32-p4-base.lib_deps}
3433
; RAM: [= ] 11.2% (used 36580 bytes from 327680 bytes)

firmware/esp32-s3.ini

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ board_build.extra_flags =
3232
build_flags = ${esp32-s3-base.build_flags}
3333
-D USE_M5UNIFIED=1
3434
-D USE_M5UNIFIEDDisplay=1
35-
-D LED_PINS=\"5,6,7,8\" ; 🚧
3635
lib_deps = ${esp32-s3-base.lib_deps}
3736
m5stack/M5Unified
3837

@@ -73,7 +72,6 @@ board_build.extra_flags =
7372
-D FT_BATTERY=1 ; battery feature
7473
-D VOLTAGE_PIN=8
7574
-D CURRENT_PIN=9
76-
-D LED_PINS=\"47,48,21,38,14,39,13,40,12,41,11,42,10,2,3,1\" ; 🚧
7775
build_flags = ${esp32-s3-base.build_flags}
7876
lib_deps = ${esp32-s3-base.lib_deps}
7977

@@ -95,7 +93,6 @@ build_flags = ${esp32-s3-base.build_flags}
9593
-D FT_BATTERY=1 ; battery feature
9694
-D VOLTAGE_PIN=8 ; 🚧
9795
-D CURRENT_PIN=9 ; 🚧
98-
-D LED_PINS=\"47,48,21,38,14,39,13,40,12,41,11,42,10,2,3,1\" ; 🚧
9996
; -D FT_ETHERNET=1 ; 🚧 not implemented yet
10097
; -D USE_M5UNIFIED=1 ;for MoonManEffect (but low on heap) crashes on this board
10198
lib_deps = ${esp32-s3-base.lib_deps}

interface/src/lib/components/moonbase/FileEdit.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@
172172
</script>
173173

174174
{#if path[0] === '/'}
175-
<Collapsible open={showEditor} class="shadow-lg" icon={null} opened={() => {}} closed={() => {}}>
175+
<Collapsible open={showEditor} class="shadow-lg" opened={() => {}} closed={() => {}}>
176176
{#snippet title()}
177177
<span>{newItem ? 'Add ' + (isFile?"file":"folder") : 'Edit ' + editableFile.name}</span>
178178
{/snippet}

interface/src/lib/components/moonbase/MultiInput.svelte

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
export let disabled = false;
2525
export let step = 1;
2626
export let changeOnInput: boolean = true;
27+
export let noPrompts: boolean = false;
2728
2829
//make getTimeAgo reactive
2930
export let currentTime = Date.now();
@@ -86,16 +87,20 @@
8687
</script>
8788

8889
<div>
89-
<div class="flex-row flex items-center space-x-2 mb-2">
90-
<label class="label cursor-pointer min-w-24" for={property.name}>
91-
<span class="mr-4">{initCap(property.name)}</span>
92-
</label>
90+
<div class="flex-row flex items-center space-x-2 mb-2">
91+
{#if !noPrompts}
92+
<label class="label cursor-pointer min-w-24" for={property.name}>
93+
<span class="mr-4">{initCap(property.name)}</span>
94+
</label>
95+
{/if}
9396

9497
{#if property.ro}
9598
{#if property.type == 'ip'}
9699
<a href="http://{value}" target="_blank">{value}</a>
97100
{:else if property.type == 'mdnsName'}
98101
<a href="http://{value}.local" target="_blank">{value}</a>
102+
{:else if property.type == 'time'}
103+
<span>{getTimeAgo(value, currentTime)}</span>
99104
{:else if property.type == 'coord3D' && value != null}
100105
<!-- value not null otherwise value.x etc can cause errors-->
101106
<span>{value.x}, {value.y}, {value.z}</span>
@@ -193,7 +198,7 @@
193198
}}
194199
/>
195200
{:else if property.type == 'time'}
196-
<span>{value} {getTimeAgo(value, currentTime)}</span>
201+
<span>{getTimeAgo(value, currentTime)}</span>
197202
{:else if property.type == 'ip'}
198203
<input
199204
type={property.type}
@@ -338,38 +343,40 @@ Adjust space-x-2 and space-y-2 for spacing. -->
338343
on:change={onChange}
339344
/>
340345
{/if}
341-
{#if !property.ro && property.default != null && property.type != 'pad'}
342-
<button
343-
type="button"
344-
class="btn btn-ghost btn-sm"
345-
disabled={disabled ||
346-
(property.type == 'coord3D'
347-
? property.default.x == value.x &&
348-
property.default.y == value.y &&
349-
property.default.z == value.z
350-
: property.default == value)}
351-
on:click={(event: any) => {
352-
if (property.type == 'coord3D') {
353-
value.x = property.default.x;
354-
value.y = property.default.y;
355-
value.z = property.default.z;
356-
} else {
357-
value = property.default;
358-
}
359-
onChange(event);
360-
}}
361-
title={'Reset to default (' +
362-
(property.type == 'coord3D'
363-
? property.default.x + ',' + property.default.y + ',' + property.default.z
364-
: property.default) +
365-
')'}>↻</button
366-
>
367-
{/if}
368-
{#if property.desc}
369-
<label class="label cursor-pointer" for={property.desc}>
370-
<!-- <span class="text-md">{initCap(property.name)}</span> -->
371-
<span class="mr-4">{initCap(property.desc)}</span>
372-
</label>
346+
{#if !noPrompts}
347+
{#if !property.ro && property.default != null && property.type != 'pad'}
348+
<button
349+
type="button"
350+
class="btn btn-ghost btn-sm"
351+
disabled={disabled ||
352+
(property.type == 'coord3D'
353+
? property.default.x == value.x &&
354+
property.default.y == value.y &&
355+
property.default.z == value.z
356+
: property.default == value)}
357+
on:click={(event: any) => {
358+
if (property.type == 'coord3D') {
359+
value.x = property.default.x;
360+
value.y = property.default.y;
361+
value.z = property.default.z;
362+
} else {
363+
value = property.default;
364+
}
365+
onChange(event);
366+
}}
367+
title={'Reset to default (' +
368+
(property.type == 'coord3D'
369+
? property.default.x + ',' + property.default.y + ',' + property.default.z
370+
: property.default) +
371+
')'}>↻</button
372+
>
373+
{/if}
374+
{#if property.desc}
375+
<label class="label cursor-pointer" for={property.desc}>
376+
<!-- <span class="text-md">{initCap(property.name)}</span> -->
377+
<span class="mr-4">{initCap(property.desc)}</span>
378+
</label>
379+
{/if}
373380
{/if}
374381
</div>
375382
</div>

interface/src/lib/components/moonbase/ObjectArray.svelte

Lines changed: 99 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@
2121
import { cubicOut } from 'svelte/easing';
2222
import SearchIcon from '~icons/tabler/search';
2323
import Delete from '~icons/tabler/trash';
24-
import { initCap, getTimeAgo } from '$lib/stores/moonbase_utilities';
24+
import { initCap } from '$lib/stores/moonbase_utilities';
2525
2626
import EditObject from './EditObject.svelte';
2727
import { modals } from 'svelte-modals';
2828
import Grip from '~icons/tabler/grip-vertical';
29+
import MultiInput from './MultiInput.svelte';
30+
import { isNumber } from 'chart.js/helpers';
2931
3032
let { property, data = $bindable(), definition, onChange, changeOnInput } = $props();
3133
3234
let dataEditable: any = $state({});
3335
34-
let propertyEditable: string = $state('');
36+
// let propertyEditable: string = $state('');
37+
38+
let searchQuery: string = $state('');
39+
searchQuery = "!none";
3540
3641
//if no records added yet, add an empty array
3742
if (data[property.name] == undefined) {
@@ -67,7 +72,7 @@
6772
}
6873
6974
function addItem(propertyName: string) {
70-
propertyEditable = propertyName;
75+
// propertyEditable = propertyName;
7176
//set the default values from the definition...
7277
dataEditable = {};
7378
@@ -114,6 +119,53 @@
114119
data[propertyName] = [...data[propertyName]]; //Trigger reactivity
115120
onChange();
116121
}
122+
123+
// Filter items based on search query - returns array of {item, originalIndex}
124+
let filteredItems = $derived.by(() => {
125+
if (!searchQuery.trim()) {
126+
return data[property.name].map((item: any, index: number) => ({
127+
item,
128+
originalIndex: index
129+
}));
130+
}
131+
132+
const isNegated = searchQuery.startsWith('!');
133+
const query = (isNegated ? searchQuery.slice(1) : searchQuery).toLowerCase().trim();
134+
135+
if (!query) {
136+
return data[property.name].map((item: any, index: number) => ({
137+
item,
138+
originalIndex: index
139+
}));
140+
}
141+
142+
const filtered = data[property.name]
143+
.map((item: any, index: number) => ({ item, originalIndex: index }))
144+
.filter(({ item }: { item: any }) => {
145+
// Search through the first 3 visible fields
146+
const matchFound = property.n.slice(0, 3).some((propertyN: any) => {
147+
let valueStr;
148+
149+
// Check dropdown - only check the SELECTED option's label, not all options
150+
if (
151+
propertyN.values &&
152+
Array.isArray(propertyN.values) &&
153+
isNumber(item[propertyN.name])
154+
) {
155+
valueStr = propertyN.values[item[propertyN.name]];
156+
} else {
157+
valueStr = item[propertyN.name];
158+
}
159+
160+
return String(valueStr).toLowerCase().includes(query);
161+
});
162+
163+
// If negated (!), return items that DON'T match
164+
return isNegated ? !matchFound : matchFound;
165+
});
166+
167+
return filtered;
168+
});
117169
</script>
118170

119171
<div class="divider mb-2 mt-0"></div>
@@ -136,37 +188,60 @@
136188
>
137189
</div>
138190

191+
<!-- Search Filter -->
192+
{#if data[property.name].length > 0}
193+
<div class="mb-3">
194+
<div class="relative">
195+
<SearchIcon class="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-base-content/50" />
196+
<input
197+
type="text"
198+
bind:value={searchQuery}
199+
placeholder="Search... (prefix with ! to exclude)"
200+
class="input input-bordered w-full input-sm"
201+
/>
202+
{#if searchQuery}
203+
<button
204+
class="btn btn-ghost btn-sm absolute right-1 top-1/2 -translate-y-1/2"
205+
onclick={() => (searchQuery = '')}
206+
>
207+
208+
</button>
209+
{/if}
210+
</div>
211+
{#if searchQuery.trim()}
212+
<div class="text-sm text-base-content/60 mt-1 ml-1">
213+
{filteredItems.length} of {data[property.name].length} items
214+
{searchQuery.startsWith('!') ? '(excluding matches)' : ''}
215+
</div>
216+
{/if}
217+
</div>
218+
{/if}
219+
139220
<div class="overflow-x-auto space-y-1" transition:slide|local={{ duration: 300, easing: cubicOut }}>
140-
<DraggableList items={data[property.name]} onReorder={handleReorder} class="space-y-2">
141-
{#snippet children({ item: item, index }: { item: any; index: number })}
221+
<DraggableList items={filteredItems} onReorder={handleReorder} class="space-y-2">
222+
{#snippet children({ item: itemWrapper, index }: { item: any; index: number })}
142223
<!-- svelte-ignore a11y_click_events_have_key_events -->
143224
<div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
144225
<Grip class="h-6 w-6 text-base-content/30 cursor-grab flex-shrink-0" />
145226
<!-- Show the first 3 fields -->
146227
{#each property.n.slice(0, 3) as propertyN}
147-
{#if propertyN.type == 'time'}
148-
<div>
149-
<div class="font-bold">
150-
{getTimeAgo(item[propertyN.name], currentTime)}
151-
</div>
152-
</div>
153-
{:else if propertyN.type == 'coord3D'}
154-
<div>
155-
<div class="font-bold">
156-
({item[propertyN.name].x}, {item[propertyN.name].y}, {item[propertyN.name].z})
157-
</div>
158-
</div>
159-
{:else if propertyN.type != 'array' && propertyN.type != 'controls' && propertyN.type != 'password'}
160-
<div>
161-
<div class="font-bold">{item[propertyN.name]}</div>
162-
</div>
228+
{#if propertyN.type != 'array' && propertyN.type != 'controls' && propertyN.type != 'password'}
229+
<MultiInput
230+
property={propertyN}
231+
bind:value={itemWrapper.item[propertyN.name]}
232+
noPrompts={true}
233+
onChange={(event) => {
234+
onChange(event);
235+
}}
236+
></MultiInput>
163237
{/if}
164238
{/each}
239+
<!-- Show nr of controls -->
165240
{#each property.n as propertyN}
166241
{#if propertyN.type == 'array' || propertyN.type == 'controls'}
167242
<div>
168243
<div class="font-bold">
169-
↓{item[propertyN.name] ? item[propertyN.name].length : ''}
244+
↓{itemWrapper.item[propertyN.name] ? itemWrapper.item[propertyN.name].length : ''}
170245
</div>
171246
</div>
172247
{/if}
@@ -177,15 +252,15 @@
177252
<button
178253
class="btn btn-ghost btn-sm"
179254
onclick={() => {
180-
handleEdit(property.name, index);
255+
handleEdit(property.name, itemWrapper.originalIndex);
181256
}}
182257
>
183258
<SearchIcon class="h-6 w-6" /></button
184259
>
185260
<button
186261
class="btn btn-ghost btn-sm"
187262
onclick={() => {
188-
deleteItem(property.name, index);
263+
deleteItem(property.name, itemWrapper.originalIndex);
189264
}}
190265
>
191266
<Delete class="text-error h-6 w-6" />

interface/vite.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ const config: UserConfig = {
1818
proxy: {
1919
// Proxying REST: http://localhost:5173/rest/bar -> http://192.168.1.83/rest/bar
2020
'/rest': {
21-
target: 'http://192.168.1.107',
21+
target: 'http://192.168.1.105',
2222
changeOrigin: true
2323
},
2424
// Proxying websockets ws://localhost:5173/ws -> ws://192.168.1.83/ws
2525
'/ws': {
26-
target: 'ws://192.168.1.107',
26+
target: 'ws://192.168.1.105',
2727
changeOrigin: true,
2828
ws: true
2929
}

0 commit comments

Comments
 (0)