@objectivex666/dsh-settings-search 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/LICENSE +674 -674
  2. package/lib/client.js +69 -22
  3. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -135,26 +135,50 @@ window.__ModuleLoader__.load({
135
135
  }
136
136
 
137
137
  /**
138
- * Read visible titles for self-drawing occupants out of the live DOM:
139
- * the shell marks every rendered inner-slot occupant with
140
- * `[data-slot="<name>"]`; when DOM order matches entry order we can
141
- * pair each rendered card with its registration id.
138
+ * Read visible titles for self-drawing occupants out of the live DOM.
139
+ * Two shell shapes exist for `[data-slot="<name>"]`:
140
+ * - per-occupant anchors: one element each, count === regs → pair ids;
141
+ * - a single WRAPPER around all rows (e.g. settings.general.item's
142
+ * stacked section): harvest each direct child as one row, keyed by
143
+ * render order ("#i") since id pairing is impossible.
142
144
  */
143
145
  const runHarvest = () => {
144
146
  let grew = false
145
147
  if (typeof document === 'undefined') return false
148
+ let map
146
149
  for (const cfg of DEEP_SLOTS) {
147
- let nodes = []
148
- try { nodes = [...document.querySelectorAll(`[data-slot="${cfg.slot}"]`)] } catch { continue }
150
+ let containers = []
151
+ try { containers = [...document.querySelectorAll(`[data-slot="${cfg.slot}"]`)] } catch { continue }
149
152
  const regs = sortEntries(entriesOf(cfg.slot))
150
- if (nodes.length === 0 || nodes.length !== regs.length) continue
151
- let map = harvest.get(cfg.slot)
153
+ if (regs.length === 0) continue
154
+ let nodes = []
155
+ if (containers.length === 1 && containers[0].childElementCount > 0) {
156
+ nodes = [...containers[0].children]
157
+ } else {
158
+ nodes = containers.filter((n) => n.childElementCount >= 0)
159
+ }
160
+ if (nodes.length === 0) continue
161
+ map = harvest.get(cfg.slot)
152
162
  if (map === undefined) { map = new Map(); harvest.set(cfg.slot, map) }
163
+ if (nodes.length !== regs.length) {
164
+ // wrapper shape: index-keyed loose harvest of every rendered row
165
+ nodes.forEach((node, i) => {
166
+ const key = '#' + i
167
+ if (map.has(key)) return
168
+ const title = titleFromNode(node)
169
+ if (title === '') return
170
+ const text = (node.textContent ?? '').replace(/\s+/g, ' ').trim().slice(0, 160)
171
+ map.set(key, { title, idx: i, text }); grew = true
172
+ })
173
+ continue
174
+ }
153
175
  regs.forEach((entry, i) => {
154
176
  const id = String(entry.options?.id ?? i)
155
177
  if (map.has(id)) return
156
178
  const title = titleFromNode(nodes[i])
157
- if (title !== '') { map.set(id, title); grew = true }
179
+ if (title === '') return
180
+ const text = (nodes[i].textContent ?? '').replace(/\s+/g, ' ').trim().slice(0, 160)
181
+ map.set(id, { title, idx: i, text }); grew = true
158
182
  })
159
183
  }
160
184
  if (grew) harvestSeq += 1
@@ -194,24 +218,30 @@ window.__ModuleLoader__.load({
194
218
  const out = []
195
219
  for (const cfg of DEEP_SLOTS) {
196
220
  const mapped = harvest.get(cfg.slot)
197
- for (const entry of sortEntries(entriesOf(cfg.slot))) {
221
+ sortEntries(entriesOf(cfg.slot)).forEach((entry, i) => {
198
222
  const id = String(entry.options?.id ?? '')
199
- if (id === '') continue
200
- const label = staticLabel(entry, cfg)
201
- || (mapped ? mapped.get(id) ?? '' : '')
202
- if (label === '') continue // unmapped, unharvested — invisible until it renders once
203
- if (out.some((e) => e.key === cfg.slot + '#' + id)) continue
223
+ if (id === '') return
224
+ // static label first, then id-paired harvest, then loose index-keyed harvest
225
+ const hitId = mapped?.get(id)
226
+ const hitIdx = mapped?.get('#' + i)
227
+ const hitObj = typeof hitId === 'object' ? hitId : (typeof hitIdx === 'object' ? hitIdx : undefined)
228
+ const label = staticLabel(entry, cfg) || hitObj?.title || ''
229
+ if (label === '') return // unmapped, unharvested — invisible until it renders once
230
+ const key = cfg.slot + '#' + id
231
+ if (out.some((e) => e.key === key)) return
204
232
  out.push({
205
233
  kind: 'option',
206
- key: cfg.slot + '#' + id,
234
+ key,
207
235
  slot: cfg.slot,
208
236
  id,
209
237
  order: entry.options?.order ?? 0,
210
238
  label,
239
+ hay: label + ' ' + (hitObj?.text ?? ''),
240
+ rowIndex: hitObj?.idx ?? i,
211
241
  parentId: cfg.parent,
212
242
  parentLabel: parentLabel(cfg.parent),
213
243
  })
214
- }
244
+ })
215
245
  }
216
246
  return out.sort((a, b) => a.parentLabel !== b.parentLabel
217
247
  ? a.parentLabel.localeCompare(b.parentLabel)
@@ -232,8 +262,10 @@ window.__ModuleLoader__.load({
232
262
  const want = String(wantText ?? '').trim()
233
263
  if (want === '') return false
234
264
  const buttons = panelButtons()
235
- const hit = buttons.find((b) => (b.textContent ?? '').trim() === want)
236
- ?? buttons.find((b) => (b.textContent ?? '').trim().startsWith(want))
265
+ const textOf = (b) => (b.textContent ?? '').trim()
266
+ const hit = buttons.find((b) => textOf(b) === want)
267
+ ?? buttons.find((b) => textOf(b).startsWith(want))
268
+ ?? buttons.find((b) => want.length >= 2 && textOf(b) !== '' && (textOf(b).includes(want) || want.includes(textOf(b))))
237
269
  if (!hit) return false
238
270
  hit.click()
239
271
  return true
@@ -261,8 +293,23 @@ window.__ModuleLoader__.load({
261
293
  /** Highlight the rendered row card for one indexed option. */
262
294
  const flashOptionRow = (item) => {
263
295
  try {
264
- const nodes = [...document.querySelectorAll(`[data-slot="${item.slot}"]`)]
265
- const target = nodes.find((n) => (n.textContent ?? '').includes(item.label)) ?? nodes[0]
296
+ let target = null
297
+ if (typeof item.rowIndex === 'number') {
298
+ // wrapper shape: pick the exact rendered row by index
299
+ const container = document.querySelector(`[data-slot="${item.slot}"]`)
300
+ target = container?.children?.[item.rowIndex] ?? null
301
+ if (target && !(target.textContent ?? '').includes(item.label)) {
302
+ // index drift — fall through to textual match below
303
+ target = null
304
+ }
305
+ }
306
+ if (!target) {
307
+ const nodes = [...document.querySelectorAll(`[data-slot="${item.slot}"]`)]
308
+ const rows = nodes.length === 1 && nodes[0].childElementCount > 0
309
+ ? [...nodes[0].children]
310
+ : nodes
311
+ target = rows.find((n) => (n.textContent ?? '').includes(item.label)) ?? null
312
+ }
266
313
  if (!target) return
267
314
  target.scrollIntoView({ block: 'nearest' })
268
315
  target.classList.remove('sss-flash')
@@ -342,7 +389,7 @@ window.__ModuleLoader__.load({
342
389
  ? sections.filter((s) => matches(`${s.label} ${s.id}`.toLowerCase()))
343
390
  : []
344
391
  const shownOptions = q !== ''
345
- ? deeps.filter((o) => matches(`${o.label} ${o.parentLabel} ${o.id} ${o.slot}`.toLowerCase()))
392
+ ? deeps.filter((o) => matches(`${o.hay} ${o.parentLabel} ${o.id} ${o.slot}`.toLowerCase()))
346
393
  : []
347
394
  const none = q !== '' && shownPages.length === 0 && shownOptions.length === 0
348
395
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectivex666/dsh-settings-search",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "DSH settings panel search plugin – instantly filter setting sections and general items with navigation guidance. Installable via `dsh plugin add`.",
5
5
  "keywords": [
6
6
  "dsh",