openvibers/openvibe.live Commits
Content: New paste opens the paste form (its header button never reached the feed's controls), and the paste box is 240px tall again (#416)
Changes
@@@ -6578,7 +6578,8 @@ body.theme-light .btn-outline:hover {justify-content: flex-end;gap: 8px;}--.paste-textarea {++/* textarea.* beats `textarea.form-input`'s 60px minimum, which had shrunk the paste box to two lines (2026-10-27). */++textarea.paste-textarea {min-height: 240px;resize: vertical;font-family: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', monospace;@@@ -471,8 +471,9 @@else if (b.dataset.cfSort && b.dataset.cfSort !== S.sort) { S.sort = b.dataset.cfSort === 'top' ? 'top' : 'new'; refilter(); }else if (b.dataset.cfWindow && b.dataset.cfWindow !== S.win && WINDOWS.includes(b.dataset.cfWindow)) { S.win = b.dataset.cfWindow; refilter(); }else if (b.hasAttribute('data-cf-new-paste')) {-- const go = () => { if (typeof openNewPasteModal === 'function') openNewPasteModal(); };-- if (window.ov) ov.load('pastes').then(go, () => {}); else go();++ // The paste form comes with the pastes feature; should it fail to load, the pastes page has the form too.++ const go = () => { if (typeof openNewPasteModal === 'function') openNewPasteModal(); else if (typeof navigate === 'function') navigate('/pastes'); };++ if (window.ov) ov.load('pastes').then(go, go); else go();}}@@@ -518,7 +519,10 @@scope.listen(st.feedEl, 'click', onFeedClick);scope.listen(wrap.querySelector('.cf-head'), 'click', (e) => {const a = e.target.closest('a[data-cf-link]');-- if (a && typeof handleLinkClick === 'function') handleLinkClick(e, a.getAttribute('href'));++ if (a) { if (typeof handleLinkClick === 'function') handleLinkClick(e, a.getAttribute('href')); return; }++ // New paste sits in the header beside AI Moments, outside the filter bar: its button goes through the same++ // controls (only .cf-bar was listened to, so it did nothing, owner 2026-10-27).++ onControl(e);});let resizeRaf = 0;scope.listen(window, 'resize', () => { cancelAnimationFrame(resizeRaf); resizeRaf = requestAnimationFrame(relayout); }, { passive: true });nnew file mode 100644@@@ -0,0 +1,32 @@++/**++ * The Content page's "New paste" (owner, 2026-10-27: "This button does nothing."). The button sits in the page header,++ * beside AI Moments, outside the filter bar the feed listened to, so its taps never reached the feed's controls; and the++ * paste box was shrunk to two lines by `textarea.form-input`'s 60px minimum.++ *++ * node test/content-new-paste.test.js++ */++'use strict';++const assert = require('assert');++const fs = require('fs');++const path = require('path');++++const read = (f) => fs.readFileSync(path.join(__dirname, '..', f), 'utf8');++const feed = read('public/js/content-feed.js');++const fragment = read('public/fragments/content.html');++++// The button is in the header, and the header's clicks go through the same controls as the filter bar's.++const head = fragment.slice(fragment.indexOf('<header class="cf-head">'), fragment.indexOf('</header>'));++assert.ok(head.includes('data-cf-new-paste'), 'New paste sits in the header');++const headListener = /scope\.listen\(wrap\.querySelector\('\.cf-head'\), 'click', \(e\) => \{[\s\S]*?\n {8}\}\);/.exec(feed);++assert.ok(headListener, 'the header has a click listener');++assert.ok(/onControl\(e\);/.test(headListener[0]), 'its clicks reach onControl, which opens the paste form');++assert.ok(feed.includes("if (window.ov) ov.load('pastes').then(go, go); else go();"), 'a failed feature load still lands on the paste form (the pastes page)');++++// The paste box keeps its 240px: a selector at least as specific as textarea.form-input, and later in the file.++const css = read('public/css/style.css');++const generic = css.indexOf('textarea.form-input { resize: vertical; min-height: 60px; }');++const paste = css.indexOf('textarea.paste-textarea {');++assert.ok(generic > -1 && paste > generic, 'textarea.paste-textarea comes after textarea.form-input');++assert.ok(/textarea\.paste-textarea \{\s*min-height: 240px;/.test(css));++++console.log('content new paste: all checks passed');
@@ -6578,7 +6578,8 @@ body.theme-light .btn-outline:hover {
@@ -6578,7 +6578,8 @@ body.theme-light .btn-outline:hover {
justify-content: flex-end;
justify-content: flex-end;
gap: 8px;
gap: 8px;
}
}
-.paste-textarea {
+/* textarea.* beats `textarea.form-input`'s 60px minimum, which had shrunk the paste box to two lines (2026-10-27). */
+textarea.paste-textarea {
min-height: 240px;
min-height: 240px;
resize: vertical;
resize: vertical;
font-family: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', monospace;
font-family: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', monospace;
@@ -471,8 +471,9 @@
@@ -471,8 +471,9 @@
else if (b.dataset.cfSort && b.dataset.cfSort !== S.sort) { S.sort = b.dataset.cfSort === 'top' ? 'top' : 'new'; refilter(); }
else if (b.dataset.cfSort && b.dataset.cfSort !== S.sort) { S.sort = b.dataset.cfSort === 'top' ? 'top' : 'new'; refilter(); }
else if (b.dataset.cfWindow && b.dataset.cfWindow !== S.win && WINDOWS.includes(b.dataset.cfWindow)) { S.win = b.dataset.cfWindow; refilter(); }
else if (b.dataset.cfWindow && b.dataset.cfWindow !== S.win && WINDOWS.includes(b.dataset.cfWindow)) { S.win = b.dataset.cfWindow; refilter(); }
else if (b.hasAttribute('data-cf-new-paste')) {
else if (b.hasAttribute('data-cf-new-paste')) {
- const go = () => { if (typeof openNewPasteModal === 'function') openNewPasteModal(); };
- if (window.ov) ov.load('pastes').then(go, () => {}); else go();
+ // The paste form comes with the pastes feature; should it fail to load, the pastes page has the form too.
+ const go = () => { if (typeof openNewPasteModal === 'function') openNewPasteModal(); else if (typeof navigate === 'function') navigate('/pastes'); };
+ if (window.ov) ov.load('pastes').then(go, go); else go();
}
}
}
}
@@ -518,7 +519,10 @@
@@ -518,7 +519,10 @@
scope.listen(st.feedEl, 'click', onFeedClick);
scope.listen(st.feedEl, 'click', onFeedClick);
scope.listen(wrap.querySelector('.cf-head'), 'click', (e) => {
scope.listen(wrap.querySelector('.cf-head'), 'click', (e) => {
const a = e.target.closest('a[data-cf-link]');
const a = e.target.closest('a[data-cf-link]');
- if (a && typeof handleLinkClick === 'function') handleLinkClick(e, a.getAttribute('href'));
+ if (a) { if (typeof handleLinkClick === 'function') handleLinkClick(e, a.getAttribute('href')); return; }
+ // New paste sits in the header beside AI Moments, outside the filter bar: its button goes through the same
+ // controls (only .cf-bar was listened to, so it did nothing, owner 2026-10-27).
+ onControl(e);
});
});
let resizeRaf = 0;
let resizeRaf = 0;
scope.listen(window, 'resize', () => { cancelAnimationFrame(resizeRaf); resizeRaf = requestAnimationFrame(relayout); }, { passive: true });
scope.listen(window, 'resize', () => { cancelAnimationFrame(resizeRaf); resizeRaf = requestAnimationFrame(relayout); }, { passive: true });
new file mode 100644
new file mode 100644
@@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
+/**
+ * The Content page's "New paste" (owner, 2026-10-27: "This button does nothing."). The button sits in the page header,
+ * beside AI Moments, outside the filter bar the feed listened to, so its taps never reached the feed's controls; and the
+ * paste box was shrunk to two lines by `textarea.form-input`'s 60px minimum.
+ *
+ * node test/content-new-paste.test.js
+ */
+'use strict';
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+
+const read = (f) => fs.readFileSync(path.join(__dirname, '..', f), 'utf8');
+const feed = read('public/js/content-feed.js');
+const fragment = read('public/fragments/content.html');
+
+// The button is in the header, and the header's clicks go through the same controls as the filter bar's.
+const head = fragment.slice(fragment.indexOf('<header class="cf-head">'), fragment.indexOf('</header>'));
+assert.ok(head.includes('data-cf-new-paste'), 'New paste sits in the header');
+const headListener = /scope\.listen\(wrap\.querySelector\('\.cf-head'\), 'click', \(e\) => \{[\s\S]*?\n {8}\}\);/.exec(feed);
+assert.ok(headListener, 'the header has a click listener');
+assert.ok(/onControl\(e\);/.test(headListener[0]), 'its clicks reach onControl, which opens the paste form');
+assert.ok(feed.includes("if (window.ov) ov.load('pastes').then(go, go); else go();"), 'a failed feature load still lands on the paste form (the pastes page)');
+
+// The paste box keeps its 240px: a selector at least as specific as textarea.form-input, and later in the file.
+const css = read('public/css/style.css');
+const generic = css.indexOf('textarea.form-input { resize: vertical; min-height: 60px; }');
+const paste = css.indexOf('textarea.paste-textarea {');
+assert.ok(generic > -1 && paste > generic, 'textarea.paste-textarea comes after textarea.form-input');
+assert.ok(/textarea\.paste-textarea \{\s*min-height: 240px;/.test(css));
+
+console.log('content new paste: all checks passed');