openvibers/openvibe.network Commits
Needs you: Review opens an approval, the text never hides under the button (#650)
Changes
@@@ -254,7 +254,8 @@ a.linked-item:hover{background:var(--bg-hover,var(--bg-card));transform:translat.ny-list { display: grid; gap: 8px; }.ny-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 12px; background: var(--bg-secondary); border: 1px solid var(--border); }.ny-ic { flex: none; width: 34px; height: 34px; display: grid; place-items: center; border-radius: 10px; background: color-mix(in srgb, var(--accent) 14%, var(--bg-input)); color: var(--accent-light); font-size: 14px; }--.ny-body { flex: 1; min-width: 0; display: grid; gap: 2px; }++.ny-body { flex: 1; min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; }++.ny-body > * { min-width: 0; }.ny-body h3 { font-size: 14px; font-weight: 750; overflow-wrap: anywhere; }.ny-body p { font-size: 12.5px; color: var(--text-secondary); line-height: 1.4; overflow-wrap: anywhere; }.ny-meta { font-size: 11.5px; color: var(--text-muted); }@@@ -262,7 +263,8 @@ a.linked-item:hover{background:var(--bg-hover,var(--bg-card));transform:translat.ny-btn:hover, .ny-btn:focus-visible { filter: brightness(1.08); outline: none; }.ny-btn:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); }.ny-btn[disabled] { opacity: .65; cursor: progress; }--@media (max-width: 600px) { .ny { padding: 16px; } .ny-item { align-items: flex-start; flex-wrap: wrap; } .ny-btn { margin-left: 46px; } }++/* A phone: the text takes the row and the button goes under it, lined up with the text (it covered the title). */++@media (max-width: 600px) { .ny { padding: 16px; } .ny-item { align-items: flex-start; flex-wrap: wrap; } .ny-body { flex: 1 1 calc(100% - 46px); } .ny-btn { margin-left: 46px; min-height: 44px; } }.td { display: grid; gap: 22px; margin: -8px 0 36px; }.td-sec { display: grid; gap: 10px; min-width: 0; }@@@ -262,9 +262,11 @@ function nyHref(item) {return /^https:\/\//.test(href) ? href : '#approvals';}++// An approval is never one tap from here: the hub shows a line, and a step may delete, spend or send. Review opens the++// approval itself (what exactly runs, Approve or Decline). Try again stays one tap: it only repeats what already ran.function nyButton(item, action) {-- const label = action === 'approve' ? 'Approve' : action === 'retry' ? 'Try again' : 'Open';-- if (action !== 'approve' && action !== 'retry') {++ const label = action === 'approve' ? 'Review' : action === 'retry' ? 'Try again' : 'Open';++ if (action !== 'retry') {const href = nyHref(item);const a = document.createElement('a');a.className = 'ny-btn';@@@ -336,7 +338,8 @@ function renderNeedsYou(doc) {summary.textContent = item.summary;body.append(summary);}-- if (item.agent) {++ // The agent by its name; a raw id (agt_…, app_…) says nothing to a person, so it is left out.++ if (item.agent && !/^(agt|app|svc|usr)[_0-9A-HJKMNP-TV-Z]{20,}$/i.test(String(item.agent))) {const meta = document.createElement('span');meta.className = 'ny-meta';meta.textContent = item.agent;@@@ -155,10 +155,21 @@ assert.match(CSS, /@media \(min-width:1000px\)\{[\s\S]*\.settings\{display:grid;assert.strictEqual(p.ids['needs-you-sub'].textContent, '2 things waiting');const rows = p.ids['needs-you-list'].children;assert.strictEqual(rows.length, 2);-- assert.strictEqual(rows[0].all((c) => c.tagName === 'BUTTON')[0].textContent, 'Approve', 'an approval has its one button');++ const review = rows[0].all((c) => c.tagName === 'A')[0];++ assert.strictEqual(review.textContent, 'Review', 'an approval opens itself: never one tap from the hub');++ assert.strictEqual(rows[0].all((c) => c.tagName === 'BUTTON').length, 0, 'no Approve button here');assert.strictEqual(rows[0].all((c) => c.tagName === 'H3')[0].textContent, 'Send one email to the venue');assert.strictEqual(rows[0].all((c) => c.tagName === 'P')[0].textContent, 'Trip planner asks to use mail.send');assert.strictEqual(rows[1].all((c) => c.tagName === 'A')[0].href, 'https://openvibe.actor/tasks/tsk_1', 'an open item links its page');++ // An agent known only by its id is not shown by it; Try again stays one tap.++ p.sandbox.renderNeedsYou({ buckets: { waiting: [++ { service: 'actor', id: 'apr_1', kind: 'approval', title: 'Run a command', agent: 'agt01M6VEKFK36XWYB3CSY1RE60YN', href: 'https://openvibe.actor/me/approvals/apr_1', actions: ['approve', 'open'] },++ { service: 'actor', id: 'tsk_2', kind: 'agent_task', title: 'Summarise the inbox', href: 'https://openvibe.actor/tasks/tsk_2', actions: ['retry', 'open'] },++ ] } });++ const two = p.ids['needs-you-list'].children;++ assert.ok(!/agt01M6/.test(two[0].textContent), 'a raw agent id is left out');++ assert.strictEqual(two[0].all((c) => c.tagName === 'A')[0].href, 'https://openvibe.actor/me/approvals/apr_1', 'Review opens the approval');++ assert.strictEqual(two[1].all((c) => c.tagName === 'BUTTON')[0].textContent, 'Try again');p.sandbox.renderNeedsYou({ buckets: { waiting: [] } });assert.strictEqual(p.ids['needs-you'].hidden, true, 'an empty queue leaves the section out');
@@ -254,7 +254,8 @@ a.linked-item:hover{background:var(--bg-hover,var(--bg-card));transform:translat
@@ -254,7 +254,8 @@ a.linked-item:hover{background:var(--bg-hover,var(--bg-card));transform:translat
.ny-list { display: grid; gap: 8px; }
.ny-list { display: grid; gap: 8px; }
.ny-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 12px; background: var(--bg-secondary); border: 1px solid var(--border); }
.ny-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 12px; background: var(--bg-secondary); border: 1px solid var(--border); }
.ny-ic { flex: none; width: 34px; height: 34px; display: grid; place-items: center; border-radius: 10px; background: color-mix(in srgb, var(--accent) 14%, var(--bg-input)); color: var(--accent-light); font-size: 14px; }
.ny-ic { flex: none; width: 34px; height: 34px; display: grid; place-items: center; border-radius: 10px; background: color-mix(in srgb, var(--accent) 14%, var(--bg-input)); color: var(--accent-light); font-size: 14px; }
-.ny-body { flex: 1; min-width: 0; display: grid; gap: 2px; }
+.ny-body { flex: 1; min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; }
+.ny-body > * { min-width: 0; }
.ny-body h3 { font-size: 14px; font-weight: 750; overflow-wrap: anywhere; }
.ny-body h3 { font-size: 14px; font-weight: 750; overflow-wrap: anywhere; }
.ny-body p { font-size: 12.5px; color: var(--text-secondary); line-height: 1.4; overflow-wrap: anywhere; }
.ny-body p { font-size: 12.5px; color: var(--text-secondary); line-height: 1.4; overflow-wrap: anywhere; }
.ny-meta { font-size: 11.5px; color: var(--text-muted); }
.ny-meta { font-size: 11.5px; color: var(--text-muted); }
@@ -262,7 +263,8 @@ a.linked-item:hover{background:var(--bg-hover,var(--bg-card));transform:translat
@@ -262,7 +263,8 @@ a.linked-item:hover{background:var(--bg-hover,var(--bg-card));transform:translat
.ny-btn:hover, .ny-btn:focus-visible { filter: brightness(1.08); outline: none; }
.ny-btn:hover, .ny-btn:focus-visible { filter: brightness(1.08); outline: none; }
.ny-btn:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); }
.ny-btn:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); }
.ny-btn[disabled] { opacity: .65; cursor: progress; }
.ny-btn[disabled] { opacity: .65; cursor: progress; }
-@media (max-width: 600px) { .ny { padding: 16px; } .ny-item { align-items: flex-start; flex-wrap: wrap; } .ny-btn { margin-left: 46px; } }
+/* A phone: the text takes the row and the button goes under it, lined up with the text (it covered the title). */
+@media (max-width: 600px) { .ny { padding: 16px; } .ny-item { align-items: flex-start; flex-wrap: wrap; } .ny-body { flex: 1 1 calc(100% - 46px); } .ny-btn { margin-left: 46px; min-height: 44px; } }
.td { display: grid; gap: 22px; margin: -8px 0 36px; }
.td { display: grid; gap: 22px; margin: -8px 0 36px; }
.td-sec { display: grid; gap: 10px; min-width: 0; }
.td-sec { display: grid; gap: 10px; min-width: 0; }
@@ -262,9 +262,11 @@ function nyHref(item) {
@@ -262,9 +262,11 @@ function nyHref(item) {
return /^https:\/\//.test(href) ? href : '#approvals';
return /^https:\/\//.test(href) ? href : '#approvals';
}
}
+// An approval is never one tap from here: the hub shows a line, and a step may delete, spend or send. Review opens the
+// approval itself (what exactly runs, Approve or Decline). Try again stays one tap: it only repeats what already ran.
function nyButton(item, action) {
function nyButton(item, action) {
- const label = action === 'approve' ? 'Approve' : action === 'retry' ? 'Try again' : 'Open';
- if (action !== 'approve' && action !== 'retry') {
+ const label = action === 'approve' ? 'Review' : action === 'retry' ? 'Try again' : 'Open';
+ if (action !== 'retry') {
const href = nyHref(item);
const href = nyHref(item);
const a = document.createElement('a');
const a = document.createElement('a');
a.className = 'ny-btn';
a.className = 'ny-btn';
@@ -336,7 +338,8 @@ function renderNeedsYou(doc) {
@@ -336,7 +338,8 @@ function renderNeedsYou(doc) {
summary.textContent = item.summary;
summary.textContent = item.summary;
body.append(summary);
body.append(summary);
}
}
- if (item.agent) {
+ // The agent by its name; a raw id (agt_…, app_…) says nothing to a person, so it is left out.
+ if (item.agent && !/^(agt|app|svc|usr)[_0-9A-HJKMNP-TV-Z]{20,}$/i.test(String(item.agent))) {
const meta = document.createElement('span');
const meta = document.createElement('span');
meta.className = 'ny-meta';
meta.className = 'ny-meta';
meta.textContent = item.agent;
meta.textContent = item.agent;
@@ -155,10 +155,21 @@ assert.match(CSS, /@media \(min-width:1000px\)\{[\s\S]*\.settings\{display:grid;
@@ -155,10 +155,21 @@ assert.match(CSS, /@media \(min-width:1000px\)\{[\s\S]*\.settings\{display:grid;
assert.strictEqual(p.ids['needs-you-sub'].textContent, '2 things waiting');
assert.strictEqual(p.ids['needs-you-sub'].textContent, '2 things waiting');
const rows = p.ids['needs-you-list'].children;
const rows = p.ids['needs-you-list'].children;
assert.strictEqual(rows.length, 2);
assert.strictEqual(rows.length, 2);
- assert.strictEqual(rows[0].all((c) => c.tagName === 'BUTTON')[0].textContent, 'Approve', 'an approval has its one button');
+ const review = rows[0].all((c) => c.tagName === 'A')[0];
+ assert.strictEqual(review.textContent, 'Review', 'an approval opens itself: never one tap from the hub');
+ assert.strictEqual(rows[0].all((c) => c.tagName === 'BUTTON').length, 0, 'no Approve button here');
assert.strictEqual(rows[0].all((c) => c.tagName === 'H3')[0].textContent, 'Send one email to the venue');
assert.strictEqual(rows[0].all((c) => c.tagName === 'H3')[0].textContent, 'Send one email to the venue');
assert.strictEqual(rows[0].all((c) => c.tagName === 'P')[0].textContent, 'Trip planner asks to use mail.send');
assert.strictEqual(rows[0].all((c) => c.tagName === 'P')[0].textContent, 'Trip planner asks to use mail.send');
assert.strictEqual(rows[1].all((c) => c.tagName === 'A')[0].href, 'https://openvibe.actor/tasks/tsk_1', 'an open item links its page');
assert.strictEqual(rows[1].all((c) => c.tagName === 'A')[0].href, 'https://openvibe.actor/tasks/tsk_1', 'an open item links its page');
+ // An agent known only by its id is not shown by it; Try again stays one tap.
+ p.sandbox.renderNeedsYou({ buckets: { waiting: [
+ { service: 'actor', id: 'apr_1', kind: 'approval', title: 'Run a command', agent: 'agt01M6VEKFK36XWYB3CSY1RE60YN', href: 'https://openvibe.actor/me/approvals/apr_1', actions: ['approve', 'open'] },
+ { service: 'actor', id: 'tsk_2', kind: 'agent_task', title: 'Summarise the inbox', href: 'https://openvibe.actor/tasks/tsk_2', actions: ['retry', 'open'] },
+ ] } });
+ const two = p.ids['needs-you-list'].children;
+ assert.ok(!/agt01M6/.test(two[0].textContent), 'a raw agent id is left out');
+ assert.strictEqual(two[0].all((c) => c.tagName === 'A')[0].href, 'https://openvibe.actor/me/approvals/apr_1', 'Review opens the approval');
+ assert.strictEqual(two[1].all((c) => c.tagName === 'BUTTON')[0].textContent, 'Try again');
p.sandbox.renderNeedsYou({ buckets: { waiting: [] } });
p.sandbox.renderNeedsYou({ buckets: { waiting: [] } });
assert.strictEqual(p.ids['needs-you'].hidden, true, 'an empty queue leaves the section out');
assert.strictEqual(p.ids['needs-you'].hidden, true, 'an empty queue leaves the section out');