Skip to content

Commit bed3bcc

Browse files
committed
Fix watcher response targeting for action updates
1 parent d256b5d commit bed3bcc

4 files changed

Lines changed: 77 additions & 99 deletions

File tree

dist/client.js

Lines changed: 42 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -792,8 +792,22 @@ function handleHtmlResponse(response, frontendTarget, frontendSwap, trigger) {
792792
}
793793
// Priority 1: Server target (by index)
794794
let explicitTarget = serverTarget;
795-
// Priority 2: Frontend target as fallback (only if no server target and not excluded)
796-
if (!explicitTarget && frontendTarget && !excluded.has(frontendTarget)) {
795+
let autoTargetSelector = null;
796+
if (!explicitTarget) {
797+
// Priority 2: auto-detect by beam-id / beam-item-id on root element
798+
const temp = document.createElement('div');
799+
temp.innerHTML = htmlItem.trim();
800+
const rootEl = temp.firstElementChild;
801+
const beamId = rootEl?.getAttribute('beam-id');
802+
const beamItemId = rootEl?.getAttribute('beam-item-id');
803+
autoTargetSelector = beamId
804+
? `[beam-id="${escapeCss(beamId)}"]`
805+
: beamItemId
806+
? `[beam-item-id="${escapeCss(beamItemId)}"]`
807+
: null;
808+
}
809+
// Priority 3: Frontend target as fallback (only if no server or auto target was found and not excluded)
810+
if (!explicitTarget && !autoTargetSelector && frontendTarget && !excluded.has(frontendTarget)) {
797811
explicitTarget = frontendTarget;
798812
}
799813
if (explicitTarget) {
@@ -806,33 +820,27 @@ function handleHtmlResponse(response, frontendTarget, frontendSwap, trigger) {
806820
console.warn(`[beam] Target "${explicitTarget}" not found on page, skipping`);
807821
}
808822
}
823+
else if (autoTargetSelector && !excluded.has(autoTargetSelector)) {
824+
const target = $(autoTargetSelector);
825+
if (target) {
826+
applyHtml(target, htmlItem.trim(), { style: 'outerHTML' });
827+
}
828+
else {
829+
console.warn(`[beam] Target "${autoTargetSelector}" (from HTML) not found on page, skipping`);
830+
}
831+
}
809832
else {
810-
// Priority 3: auto-detect by id / beam-id / beam-item-id on root element
811-
// Each identifier works on its own. If multiple are present, we use a deterministic
812-
// priority order so only *one* target is selected.
813-
const temp = document.createElement('div');
814-
temp.innerHTML = htmlItem.trim();
815-
const rootEl = temp.firstElementChild;
816-
const id = rootEl instanceof HTMLElement ? rootEl.id : '';
817-
const beamId = rootEl?.getAttribute('beam-id');
818-
const beamItemId = rootEl?.getAttribute('beam-item-id');
819-
const selector = id
820-
? `#${escapeCss(id)}`
821-
: beamId
822-
? `[beam-id="${escapeCss(beamId)}"]`
823-
: beamItemId
824-
? `[beam-item-id="${escapeCss(beamItemId)}"]`
825-
: null;
826-
if (selector && !excluded.has(selector)) {
827-
const target = $(selector);
833+
// If no beam-id/beam-item-id target is found, allow the triggering element's frontend target as the last fallback.
834+
if (frontendTarget && !excluded.has(frontendTarget)) {
835+
const target = $(frontendTarget);
828836
if (target) {
829-
applyHtml(target, htmlItem.trim(), { style: 'outerHTML' });
837+
swap(target, htmlItem, swapMode, trigger);
830838
}
831839
else {
832-
console.warn(`[beam] Target "${selector}" (from HTML) not found on page, skipping`);
840+
console.warn(`[beam] Target "${frontendTarget}" not found on page, skipping`);
833841
}
834842
}
835-
// If no id/beam-id/beam-item-id found or excluded, skip silently
843+
// If no target found or all candidates are excluded, skip silently.
836844
}
837845
});
838846
}
@@ -2160,38 +2168,19 @@ function setupInputWatcher(el) {
21602168
htmlEl.setAttribute('beam-touched', '');
21612169
try {
21622170
const stream = await api.call(action, params);
2163-
const reader = stream.getReader();
2164-
try {
2165-
while (true) {
2166-
const { done, value } = await reader.read();
2167-
if (done)
2168-
break;
2169-
if (value.html && targetSelector) {
2170-
const targets = $$(targetSelector);
2171-
const htmlArray = Array.isArray(value.html) ? value.html : [value.html];
2172-
targets.forEach((target, i) => {
2173-
const html = htmlArray[i] || htmlArray[0];
2174-
if (html)
2175-
swap(target, html, swapMode);
2176-
});
2177-
}
2178-
if (value.html) {
2179-
const htmlStr = Array.isArray(value.html) ? value.html.join('') : value.html;
2180-
const { oob } = parseOobSwaps(htmlStr);
2181-
for (const { selector, content, swapMode: oobSwapMode } of oob) {
2182-
const oobTarget = $(selector);
2183-
if (oobTarget)
2184-
swap(oobTarget, content, oobSwapMode || 'replace');
2185-
}
2186-
}
2187-
if (value.script)
2188-
executeScript(value.script);
2189-
}
2190-
}
2191-
finally {
2192-
reader.releaseLock();
2171+
const reader = stream.getReader();
2172+
try {
2173+
while (true) {
2174+
const { done, value } = await reader.read();
2175+
if (done)
2176+
break;
2177+
applyResponse(value, targetSelector, swapMode, htmlEl);
21932178
}
21942179
}
2180+
finally {
2181+
reader.releaseLock();
2182+
}
2183+
}
21952184
catch (err) {
21962185
console.error('Input watcher error:', err);
21972186
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@benqoder/beam",
3-
"version": "0.6.0",
3+
"version": "0.6.1",
44
"type": "module",
55
"publishConfig": {
66
"registry": "https://registry.npmjs.org",

src/client.ts

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,24 @@ function handleHtmlResponse(
917917
// Priority 1: Server target (by index)
918918
let explicitTarget = serverTarget
919919

920-
// Priority 2: Frontend target as fallback (only if no server target and not excluded)
921-
if (!explicitTarget && frontendTarget && !excluded.has(frontendTarget)) {
920+
let autoTargetSelector: string | null = null
921+
if (!explicitTarget) {
922+
// Priority 2: auto-detect by beam-id / beam-item-id on root element
923+
const temp = document.createElement('div')
924+
temp.innerHTML = htmlItem.trim()
925+
const rootEl = temp.firstElementChild
926+
const beamId = rootEl?.getAttribute('beam-id')
927+
const beamItemId = rootEl?.getAttribute('beam-item-id')
928+
929+
autoTargetSelector = beamId
930+
? `[beam-id="${escapeCss(beamId)}"]`
931+
: beamItemId
932+
? `[beam-item-id="${escapeCss(beamItemId)}"]`
933+
: null
934+
}
935+
936+
// Priority 3: Frontend target as fallback (only if no server or auto target was found and not excluded)
937+
if (!explicitTarget && !autoTargetSelector && frontendTarget && !excluded.has(frontendTarget)) {
922938
explicitTarget = frontendTarget
923939
}
924940

@@ -930,35 +946,24 @@ function handleHtmlResponse(
930946
} else {
931947
console.warn(`[beam] Target "${explicitTarget}" not found on page, skipping`)
932948
}
949+
} else if (autoTargetSelector && !excluded.has(autoTargetSelector)) {
950+
const target = $(autoTargetSelector)
951+
if (target) {
952+
applyHtml(target, htmlItem.trim(), { style: 'outerHTML' })
953+
} else {
954+
console.warn(`[beam] Target "${autoTargetSelector}" (from HTML) not found on page, skipping`)
955+
}
933956
} else {
934-
// Priority 3: auto-detect by id / beam-id / beam-item-id on root element
935-
// Each identifier works on its own. If multiple are present, we use a deterministic
936-
// priority order so only *one* target is selected.
937-
const temp = document.createElement('div')
938-
temp.innerHTML = htmlItem.trim()
939-
const rootEl = temp.firstElementChild
940-
941-
const id = rootEl instanceof HTMLElement ? rootEl.id : ''
942-
const beamId = rootEl?.getAttribute('beam-id')
943-
const beamItemId = rootEl?.getAttribute('beam-item-id')
944-
945-
const selector = id
946-
? `#${escapeCss(id)}`
947-
: beamId
948-
? `[beam-id="${escapeCss(beamId)}"]`
949-
: beamItemId
950-
? `[beam-item-id="${escapeCss(beamItemId)}"]`
951-
: null
952-
953-
if (selector && !excluded.has(selector)) {
954-
const target = $(selector)
957+
// If no beam-id/beam-item-id target is found, allow the triggering element's frontend target as the last fallback.
958+
if (frontendTarget && !excluded.has(frontendTarget)) {
959+
const target = $(frontendTarget)
955960
if (target) {
956-
applyHtml(target, htmlItem.trim(), { style: 'outerHTML' })
961+
swap(target, htmlItem, swapMode, trigger)
957962
} else {
958-
console.warn(`[beam] Target "${selector}" (from HTML) not found on page, skipping`)
963+
console.warn(`[beam] Target "${frontendTarget}" not found on page, skipping`)
959964
}
960965
}
961-
// If no id/beam-id/beam-item-id found or excluded, skip silently
966+
// If no target found or all candidates are excluded, skip silently.
962967
}
963968
})
964969
}
@@ -2438,23 +2443,7 @@ function setupInputWatcher(el: Element): void {
24382443
while (true) {
24392444
const { done, value } = await reader.read()
24402445
if (done) break
2441-
if (value.html && targetSelector) {
2442-
const targets = $$(targetSelector)
2443-
const htmlArray = Array.isArray(value.html) ? value.html : [value.html]
2444-
targets.forEach((target, i) => {
2445-
const html = htmlArray[i] || htmlArray[0]
2446-
if (html) swap(target, html, swapMode)
2447-
})
2448-
}
2449-
if (value.html) {
2450-
const htmlStr = Array.isArray(value.html) ? value.html.join('') : value.html
2451-
const { oob } = parseOobSwaps(htmlStr)
2452-
for (const { selector, content, swapMode: oobSwapMode } of oob) {
2453-
const oobTarget = $(selector)
2454-
if (oobTarget) swap(oobTarget, content, oobSwapMode || 'replace')
2455-
}
2456-
}
2457-
if (value.script) executeScript(value.script)
2446+
applyResponse(value, targetSelector, swapMode, htmlEl)
24582447
}
24592448
} finally {
24602449
reader.releaseLock()

0 commit comments

Comments
 (0)