openvibers/openvibe.actor Commits
Actor: choose computers from Link (#406)
Changes
@@@ -2,6 +2,12 @@'use strict';if (!document.querySelector('[data-actor-page]')) return;const $ = s => document.querySelector(s);++ if (document.querySelector('[data-ov-machines]') && !window.OpenVibeMachines) {++ const picker = document.createElement('script');++ picker.src = '/shared/machines.js';++ picker.defer = true;++ document.head.append(picker);++ }const api = '/api/v1/me/actor';let controlling = false, refreshing = false, oldFrame = null, cursor = null;function notice(text, error) { const n = $('#ay-notice'); if (n) { n.textContent = text; n.classList.toggle('ay-error', !!error); } }@@@ -66,7 +66,8 @@ function render(data, viewer = {}) {+ [['find_work','Find work','It looks for roles that fit you and brings the best three.','flag'],['brief_me','Brief me','A short morning read on what you follow.','book'],['watch_deals','Watch for deals','It watches a price and tells you when it drops.','tag']].map(([l,t,d,ic]) => '<button type="button" class="ay-starter" data-lane="' + l + '"><span class="ay-starter-i" aria-hidden="true">' + kit.icon(ic) + '</span><span><b>' + t + '</b><small>' + d + '</small></span></button>').join('')+ '</div>' + button('Something else','data-ovk-sheet="#ay-lanes"') + '</div>'));const moneyHtml = card('ay-money','Money','<div class="ay-money-total"><div><span>Model spend today</span><strong>' + money(now.cost_today) + '</strong></div><div><span>Confirmed earnings</span><strong>' + money(earnings.reduce((n,x)=>n+Number(x.usd),0)) + '</strong></div></div><p class="ay-muted"><a href="https://openvibe.network/wallet">Model and computer charges ↗</a></p><details><summary>Model spending</summary><ul>' + journal.filter(j=>Number(j.cost_usd)>0).map(j=>'<li>' + time(j.created_at) + ' · ' + e(j.text) + ' · ' + money(j.cost_usd) + '</li>').join('') + '</ul></details>' + (a.computer.kind==='openvibe'?'<div id="ay-computer-time"></div>' + button('Show computer time','data-computer-time'):'') + '<h3>This week · cost per result</h3>' + (weekly.map(w => { const n = Number(w.results || 0); return '<div class="ay-week"><span>' + e(labels[w.lane]) + '</span><meter min="0" max="' + Math.max(1,...weekly.map(x=>x.results?Number(x.usd)/x.results:0)) + '" value="' + (n?Number(w.usd)/n:0) + '" aria-label="' + e(labels[w.lane]) + ' cost"></meter><span>' + (n ? money(Number(w.usd)/n) : money(w.usd) + ' · no result yet') + '</span></div>'; }).join('') || '<p class="ay-muted">Your first result starts the weekly view.</p>') + '<details><summary>Confirmed earnings <span class="ovk-badge">' + earnings.length + '</span></summary><ul>' + earnings.map(x=>'<li>' + e(x.text) + ' · ' + money(x.usd) + '</li>').join('') + '</ul></details><form id="ay-earn">' + input('text','What did you earn?','required maxlength="500" placeholder="A paid project"') + input('usd','Amount in USD','type="number" min="0.01" step="0.01" required') + '<label class="ay-check"><input name="confirmed" type="checkbox" required> I received this money</label><button class="ovk-btn">Add earnings</button></form>');-- const computerHtml = card('ay-computer','Computer','<p class="ay-current">' + e(pc) + '</p><form id="ay-computer-form"><label>Where it works<select name="kind">' + [['none','No computer'],['openvibe','OpenVibe computer'],['link','Your Link computer']].map(([k,v])=>'<option value="' + k + '" ' + (a.computer.kind===k?'selected':'') + '>' + v + '</option>').join('') + '</select></label>' + input('machine_id','Link machine ID','value="' + e(a.computer.machine_id || '') + '" placeholder="Choose a computer from Link"') + '<button class="ovk-btn">Save computer</button> <a href="https://openvibe.link">Open Link ↗</a></form><h3>Signed in to</h3><div id="ay-sites"><p class="ay-muted">' + (a.computer.kind==='openvibe'?'Open saved sites to see its sign-ins.':'Sign-ins stay on your selected computer.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show saved sites','data-sites'):'') + '<details><summary>Take-over history</summary><div id="ay-history"><p class="ay-muted">' + (session?'Open the history to see who used this screen.':'No computer is open.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show history','data-history'):'') + '</details>');++ const machineChoice = viewer.machine || a.computer.machine_id || '';++ const computerHtml = card('ay-computer','Computer','<p class="ay-current">' + e(pc) + '</p><form id="ay-computer-form"><label>Where it works<select name="kind">' + [['none','No computer'],['openvibe','OpenVibe computer'],['link','Your Link computer']].map(([k,v])=>'<option value="' + k + '" ' + (a.computer.kind===k?'selected':'') + '>' + v + '</option>').join('') + '</select></label><div data-ov-machines data-needs="desktop" data-name="machine_id" data-value="' + e(machineChoice) + '"></div><button class="ovk-btn">Save computer</button> <a href="https://openvibe.link/add-computer?return_to=' + encodeURIComponent('https://openvibe.actor/you') + '">Add a computer</a></form><h3>Signed in to</h3><div id="ay-sites"><p class="ay-muted">' + (a.computer.kind==='openvibe'?'Open saved sites to see its sign-ins.':'Sign-ins stay on your selected computer.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show saved sites','data-sites'):'') + '<details><summary>Take-over history</summary><div id="ay-history"><p class="ay-muted">' + (session?'Open the history to see who used this screen.':'No computer is open.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show history','data-history'):'') + '</details>');const modelButton = (id,text,sub='') => '<button type="button" class="ay-model ovk-btn ovk-btn--quiet" data-brain="' + e(id) + '"><span>' + e(text) + '</span><small>' + e(sub) + '</small>' + (a.brain===id?'<span aria-label="Selected">✓</span>':'') + '</button>';const brain = kit.sheet({id:'ay-brain',title:'Brain',body:'<p>From the next step.</p><label>Use for<select id="ay-brain-goal"><option value="">Your Actor</option>' + goals.map(g=>'<option value="' + e(g.id) + '">' + e(g.text) + '</option>').join('') + '</select></label>' + modelButton('auto','Auto','Recommended') + '<h3>OpenVibe</h3>' + modelButton('openvibe','OpenVibe','Chooses a model for the step') + models.filter(m=>!/uncensored|18\+/i.test(JSON.stringify(m))).map(m=>modelButton(m.id,m.name||m.id,m.hourly_usd!=null?money(m.hourly_usd)+'/hour of work':require('../runtime/ai-catalog').priceDescription(m.output_nano_usd_per_m))).join('') + '<h3>Your keys</h3><p class="ay-muted">No OpenVibe model charge. Your provider may bill you.</p>' + ['openai','anthropic','deepseek','google','openrouter','mistral'].map(p=>modelButton('own:'+p,p[0].toUpperCase()+p.slice(1))).join('') + '<a href="/models">Manage your keys ↗</a><h3>Your plans</h3><a class="ovk-btn ovk-btn--quiet" href="https://openvibe.codes">Connect your Codex plan ↗</a> <a class="ovk-btn ovk-btn--quiet" href="https://openvibe.codes">Connect your Claude Code plan ↗</a>' + (a.adult_opt_in?'<h3>Uncensored · 18+</h3>' + models.filter(m=>/uncensored|18\+/i.test(JSON.stringify(m))).map(m=>modelButton(m.id,m.name||m.id)).join(''):'') + '<h3>Local</h3>' + modelButton('own:node','Your Link computer','No model charge') + '<label class="ay-check"><input type="checkbox" data-adult ' + (a.adult_opt_in?'checked':'') + '> I am 18 or older and want to see adult models</label>'});const lanes = kit.sheet({id:'ay-lanes',title:'What should your Actor do?',body:'<div class="ay-lanes">' + ['find_work','earn_gig','grow_channel','brief_me','watch_deals'].map(l=>'<button type="button" class="ay-lane ovk-btn" data-lane="' + l + '">' + thread.icon('step') + '<span>' + labels[l] + '</span></button>').join('') + '</div>' + button('Something else','data-lane="custom"')});@@@ -81,7 +82,7 @@ function router(ctx) {if(req.viewer?.kind!=='user'||!req.viewer.subject) return res.redirect('/auth/login?next=%2Fyou');const data = await ctx.day.pageData('user:'+req.viewer.subject);res.set('Cache-Control','private, no-store');-- const body = render(data,req.viewer);++ const body = render(data,{ ...req.viewer, machine: req.query.machine });if(req.query.fragment==='1') return res.type('html').send(body);send(res,200,{title:'Your Actor',viewer:req.viewer,config:ctx.config,path:'/you',body:raw(body),styles:['kit.css','thread.css'],css:['css/you.css'],sharedScripts:['kit-web.js','ov-thread.js'],scripts:['js/you.js']});});@@@ -19,6 +19,10 @@ const SAME = { 'sec-fetch-site':'same-origin' };else {assert.doesNotMatch(render({...base,actor}),/id="ay-screen"/);assert.match(render({...base,actor,session_id:'ses_01J8Z4M2Q0R7T9YV3K6N8P1W2A'}),/id="ay-screen"/);}}assert.doesNotMatch(render(base),/Uncensored/);++ const computer = render(base);++ assert.match(computer, /data-ov-machines[^>]*data-needs="desktop"[^>]*data-name="machine_id"/);++ assert.doesNotMatch(computer, /Link machine ID/);++ assert.match(render(base, { machine: 'dev_01J8Z4M2Q0R7T9YV3K6N8P1W2A' }), /data-value="dev_01J8Z4M2Q0R7T9YV3K6N8P1W2A"/);assert.doesNotMatch(render({...base, actor:{...a, active_task:'tsk_01J8Z4M2Q0R7T9YV3K6N8P1W2A'}, model_switch_enabled:false}),/ay-run-model/);// On: one row in Now. The label says what it changes, the button is one word, the choices are grouped by who runs// them and named as the chat's model menu names them.
@@ -2,6 +2,12 @@
@@ -2,6 +2,12 @@
'use strict';
'use strict';
if (!document.querySelector('[data-actor-page]')) return;
if (!document.querySelector('[data-actor-page]')) return;
const $ = s => document.querySelector(s);
const $ = s => document.querySelector(s);
+ if (document.querySelector('[data-ov-machines]') && !window.OpenVibeMachines) {
+ const picker = document.createElement('script');
+ picker.src = '/shared/machines.js';
+ picker.defer = true;
+ document.head.append(picker);
+ }
const api = '/api/v1/me/actor';
const api = '/api/v1/me/actor';
let controlling = false, refreshing = false, oldFrame = null, cursor = null;
let controlling = false, refreshing = false, oldFrame = null, cursor = null;
function notice(text, error) { const n = $('#ay-notice'); if (n) { n.textContent = text; n.classList.toggle('ay-error', !!error); } }
function notice(text, error) { const n = $('#ay-notice'); if (n) { n.textContent = text; n.classList.toggle('ay-error', !!error); } }
@@ -66,7 +66,8 @@ function render(data, viewer = {}) {
@@ -66,7 +66,8 @@ function render(data, viewer = {}) {
+ [['find_work','Find work','It looks for roles that fit you and brings the best three.','flag'],['brief_me','Brief me','A short morning read on what you follow.','book'],['watch_deals','Watch for deals','It watches a price and tells you when it drops.','tag']].map(([l,t,d,ic]) => '<button type="button" class="ay-starter" data-lane="' + l + '"><span class="ay-starter-i" aria-hidden="true">' + kit.icon(ic) + '</span><span><b>' + t + '</b><small>' + d + '</small></span></button>').join('')
+ [['find_work','Find work','It looks for roles that fit you and brings the best three.','flag'],['brief_me','Brief me','A short morning read on what you follow.','book'],['watch_deals','Watch for deals','It watches a price and tells you when it drops.','tag']].map(([l,t,d,ic]) => '<button type="button" class="ay-starter" data-lane="' + l + '"><span class="ay-starter-i" aria-hidden="true">' + kit.icon(ic) + '</span><span><b>' + t + '</b><small>' + d + '</small></span></button>').join('')
+ '</div>' + button('Something else','data-ovk-sheet="#ay-lanes"') + '</div>'));
+ '</div>' + button('Something else','data-ovk-sheet="#ay-lanes"') + '</div>'));
const moneyHtml = card('ay-money','Money','<div class="ay-money-total"><div><span>Model spend today</span><strong>' + money(now.cost_today) + '</strong></div><div><span>Confirmed earnings</span><strong>' + money(earnings.reduce((n,x)=>n+Number(x.usd),0)) + '</strong></div></div><p class="ay-muted"><a href="https://openvibe.network/wallet">Model and computer charges ↗</a></p><details><summary>Model spending</summary><ul>' + journal.filter(j=>Number(j.cost_usd)>0).map(j=>'<li>' + time(j.created_at) + ' · ' + e(j.text) + ' · ' + money(j.cost_usd) + '</li>').join('') + '</ul></details>' + (a.computer.kind==='openvibe'?'<div id="ay-computer-time"></div>' + button('Show computer time','data-computer-time'):'') + '<h3>This week · cost per result</h3>' + (weekly.map(w => { const n = Number(w.results || 0); return '<div class="ay-week"><span>' + e(labels[w.lane]) + '</span><meter min="0" max="' + Math.max(1,...weekly.map(x=>x.results?Number(x.usd)/x.results:0)) + '" value="' + (n?Number(w.usd)/n:0) + '" aria-label="' + e(labels[w.lane]) + ' cost"></meter><span>' + (n ? money(Number(w.usd)/n) : money(w.usd) + ' · no result yet') + '</span></div>'; }).join('') || '<p class="ay-muted">Your first result starts the weekly view.</p>') + '<details><summary>Confirmed earnings <span class="ovk-badge">' + earnings.length + '</span></summary><ul>' + earnings.map(x=>'<li>' + e(x.text) + ' · ' + money(x.usd) + '</li>').join('') + '</ul></details><form id="ay-earn">' + input('text','What did you earn?','required maxlength="500" placeholder="A paid project"') + input('usd','Amount in USD','type="number" min="0.01" step="0.01" required') + '<label class="ay-check"><input name="confirmed" type="checkbox" required> I received this money</label><button class="ovk-btn">Add earnings</button></form>');
const moneyHtml = card('ay-money','Money','<div class="ay-money-total"><div><span>Model spend today</span><strong>' + money(now.cost_today) + '</strong></div><div><span>Confirmed earnings</span><strong>' + money(earnings.reduce((n,x)=>n+Number(x.usd),0)) + '</strong></div></div><p class="ay-muted"><a href="https://openvibe.network/wallet">Model and computer charges ↗</a></p><details><summary>Model spending</summary><ul>' + journal.filter(j=>Number(j.cost_usd)>0).map(j=>'<li>' + time(j.created_at) + ' · ' + e(j.text) + ' · ' + money(j.cost_usd) + '</li>').join('') + '</ul></details>' + (a.computer.kind==='openvibe'?'<div id="ay-computer-time"></div>' + button('Show computer time','data-computer-time'):'') + '<h3>This week · cost per result</h3>' + (weekly.map(w => { const n = Number(w.results || 0); return '<div class="ay-week"><span>' + e(labels[w.lane]) + '</span><meter min="0" max="' + Math.max(1,...weekly.map(x=>x.results?Number(x.usd)/x.results:0)) + '" value="' + (n?Number(w.usd)/n:0) + '" aria-label="' + e(labels[w.lane]) + ' cost"></meter><span>' + (n ? money(Number(w.usd)/n) : money(w.usd) + ' · no result yet') + '</span></div>'; }).join('') || '<p class="ay-muted">Your first result starts the weekly view.</p>') + '<details><summary>Confirmed earnings <span class="ovk-badge">' + earnings.length + '</span></summary><ul>' + earnings.map(x=>'<li>' + e(x.text) + ' · ' + money(x.usd) + '</li>').join('') + '</ul></details><form id="ay-earn">' + input('text','What did you earn?','required maxlength="500" placeholder="A paid project"') + input('usd','Amount in USD','type="number" min="0.01" step="0.01" required') + '<label class="ay-check"><input name="confirmed" type="checkbox" required> I received this money</label><button class="ovk-btn">Add earnings</button></form>');
- const computerHtml = card('ay-computer','Computer','<p class="ay-current">' + e(pc) + '</p><form id="ay-computer-form"><label>Where it works<select name="kind">' + [['none','No computer'],['openvibe','OpenVibe computer'],['link','Your Link computer']].map(([k,v])=>'<option value="' + k + '" ' + (a.computer.kind===k?'selected':'') + '>' + v + '</option>').join('') + '</select></label>' + input('machine_id','Link machine ID','value="' + e(a.computer.machine_id || '') + '" placeholder="Choose a computer from Link"') + '<button class="ovk-btn">Save computer</button> <a href="https://openvibe.link">Open Link ↗</a></form><h3>Signed in to</h3><div id="ay-sites"><p class="ay-muted">' + (a.computer.kind==='openvibe'?'Open saved sites to see its sign-ins.':'Sign-ins stay on your selected computer.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show saved sites','data-sites'):'') + '<details><summary>Take-over history</summary><div id="ay-history"><p class="ay-muted">' + (session?'Open the history to see who used this screen.':'No computer is open.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show history','data-history'):'') + '</details>');
+ const machineChoice = viewer.machine || a.computer.machine_id || '';
+ const computerHtml = card('ay-computer','Computer','<p class="ay-current">' + e(pc) + '</p><form id="ay-computer-form"><label>Where it works<select name="kind">' + [['none','No computer'],['openvibe','OpenVibe computer'],['link','Your Link computer']].map(([k,v])=>'<option value="' + k + '" ' + (a.computer.kind===k?'selected':'') + '>' + v + '</option>').join('') + '</select></label><div data-ov-machines data-needs="desktop" data-name="machine_id" data-value="' + e(machineChoice) + '"></div><button class="ovk-btn">Save computer</button> <a href="https://openvibe.link/add-computer?return_to=' + encodeURIComponent('https://openvibe.actor/you') + '">Add a computer</a></form><h3>Signed in to</h3><div id="ay-sites"><p class="ay-muted">' + (a.computer.kind==='openvibe'?'Open saved sites to see its sign-ins.':'Sign-ins stay on your selected computer.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show saved sites','data-sites'):'') + '<details><summary>Take-over history</summary><div id="ay-history"><p class="ay-muted">' + (session?'Open the history to see who used this screen.':'No computer is open.') + '</p></div>' + (a.computer.kind==='openvibe'?button('Show history','data-history'):'') + '</details>');
const modelButton = (id,text,sub='') => '<button type="button" class="ay-model ovk-btn ovk-btn--quiet" data-brain="' + e(id) + '"><span>' + e(text) + '</span><small>' + e(sub) + '</small>' + (a.brain===id?'<span aria-label="Selected">✓</span>':'') + '</button>';
const modelButton = (id,text,sub='') => '<button type="button" class="ay-model ovk-btn ovk-btn--quiet" data-brain="' + e(id) + '"><span>' + e(text) + '</span><small>' + e(sub) + '</small>' + (a.brain===id?'<span aria-label="Selected">✓</span>':'') + '</button>';
const brain = kit.sheet({id:'ay-brain',title:'Brain',body:'<p>From the next step.</p><label>Use for<select id="ay-brain-goal"><option value="">Your Actor</option>' + goals.map(g=>'<option value="' + e(g.id) + '">' + e(g.text) + '</option>').join('') + '</select></label>' + modelButton('auto','Auto','Recommended') + '<h3>OpenVibe</h3>' + modelButton('openvibe','OpenVibe','Chooses a model for the step') + models.filter(m=>!/uncensored|18\+/i.test(JSON.stringify(m))).map(m=>modelButton(m.id,m.name||m.id,m.hourly_usd!=null?money(m.hourly_usd)+'/hour of work':require('../runtime/ai-catalog').priceDescription(m.output_nano_usd_per_m))).join('') + '<h3>Your keys</h3><p class="ay-muted">No OpenVibe model charge. Your provider may bill you.</p>' + ['openai','anthropic','deepseek','google','openrouter','mistral'].map(p=>modelButton('own:'+p,p[0].toUpperCase()+p.slice(1))).join('') + '<a href="/models">Manage your keys ↗</a><h3>Your plans</h3><a class="ovk-btn ovk-btn--quiet" href="https://openvibe.codes">Connect your Codex plan ↗</a> <a class="ovk-btn ovk-btn--quiet" href="https://openvibe.codes">Connect your Claude Code plan ↗</a>' + (a.adult_opt_in?'<h3>Uncensored · 18+</h3>' + models.filter(m=>/uncensored|18\+/i.test(JSON.stringify(m))).map(m=>modelButton(m.id,m.name||m.id)).join(''):'') + '<h3>Local</h3>' + modelButton('own:node','Your Link computer','No model charge') + '<label class="ay-check"><input type="checkbox" data-adult ' + (a.adult_opt_in?'checked':'') + '> I am 18 or older and want to see adult models</label>'});
const brain = kit.sheet({id:'ay-brain',title:'Brain',body:'<p>From the next step.</p><label>Use for<select id="ay-brain-goal"><option value="">Your Actor</option>' + goals.map(g=>'<option value="' + e(g.id) + '">' + e(g.text) + '</option>').join('') + '</select></label>' + modelButton('auto','Auto','Recommended') + '<h3>OpenVibe</h3>' + modelButton('openvibe','OpenVibe','Chooses a model for the step') + models.filter(m=>!/uncensored|18\+/i.test(JSON.stringify(m))).map(m=>modelButton(m.id,m.name||m.id,m.hourly_usd!=null?money(m.hourly_usd)+'/hour of work':require('../runtime/ai-catalog').priceDescription(m.output_nano_usd_per_m))).join('') + '<h3>Your keys</h3><p class="ay-muted">No OpenVibe model charge. Your provider may bill you.</p>' + ['openai','anthropic','deepseek','google','openrouter','mistral'].map(p=>modelButton('own:'+p,p[0].toUpperCase()+p.slice(1))).join('') + '<a href="/models">Manage your keys ↗</a><h3>Your plans</h3><a class="ovk-btn ovk-btn--quiet" href="https://openvibe.codes">Connect your Codex plan ↗</a> <a class="ovk-btn ovk-btn--quiet" href="https://openvibe.codes">Connect your Claude Code plan ↗</a>' + (a.adult_opt_in?'<h3>Uncensored · 18+</h3>' + models.filter(m=>/uncensored|18\+/i.test(JSON.stringify(m))).map(m=>modelButton(m.id,m.name||m.id)).join(''):'') + '<h3>Local</h3>' + modelButton('own:node','Your Link computer','No model charge') + '<label class="ay-check"><input type="checkbox" data-adult ' + (a.adult_opt_in?'checked':'') + '> I am 18 or older and want to see adult models</label>'});
const lanes = kit.sheet({id:'ay-lanes',title:'What should your Actor do?',body:'<div class="ay-lanes">' + ['find_work','earn_gig','grow_channel','brief_me','watch_deals'].map(l=>'<button type="button" class="ay-lane ovk-btn" data-lane="' + l + '">' + thread.icon('step') + '<span>' + labels[l] + '</span></button>').join('') + '</div>' + button('Something else','data-lane="custom"')});
const lanes = kit.sheet({id:'ay-lanes',title:'What should your Actor do?',body:'<div class="ay-lanes">' + ['find_work','earn_gig','grow_channel','brief_me','watch_deals'].map(l=>'<button type="button" class="ay-lane ovk-btn" data-lane="' + l + '">' + thread.icon('step') + '<span>' + labels[l] + '</span></button>').join('') + '</div>' + button('Something else','data-lane="custom"')});
@@ -81,7 +82,7 @@ function router(ctx) {
@@ -81,7 +82,7 @@ function router(ctx) {
if(req.viewer?.kind!=='user'||!req.viewer.subject) return res.redirect('/auth/login?next=%2Fyou');
if(req.viewer?.kind!=='user'||!req.viewer.subject) return res.redirect('/auth/login?next=%2Fyou');
const data = await ctx.day.pageData('user:'+req.viewer.subject);
const data = await ctx.day.pageData('user:'+req.viewer.subject);
res.set('Cache-Control','private, no-store');
res.set('Cache-Control','private, no-store');
- const body = render(data,req.viewer);
+ const body = render(data,{ ...req.viewer, machine: req.query.machine });
if(req.query.fragment==='1') return res.type('html').send(body);
if(req.query.fragment==='1') return res.type('html').send(body);
send(res,200,{title:'Your Actor',viewer:req.viewer,config:ctx.config,path:'/you',body:raw(body),styles:['kit.css','thread.css'],css:['css/you.css'],sharedScripts:['kit-web.js','ov-thread.js'],scripts:['js/you.js']});
send(res,200,{title:'Your Actor',viewer:req.viewer,config:ctx.config,path:'/you',body:raw(body),styles:['kit.css','thread.css'],css:['css/you.css'],sharedScripts:['kit-web.js','ov-thread.js'],scripts:['js/you.js']});
});
});
@@ -19,6 +19,10 @@ const SAME = { 'sec-fetch-site':'same-origin' };
@@ -19,6 +19,10 @@ const SAME = { 'sec-fetch-site':'same-origin' };
else {assert.doesNotMatch(render({...base,actor}),/id="ay-screen"/);assert.match(render({...base,actor,session_id:'ses_01J8Z4M2Q0R7T9YV3K6N8P1W2A'}),/id="ay-screen"/);}
else {assert.doesNotMatch(render({...base,actor}),/id="ay-screen"/);assert.match(render({...base,actor,session_id:'ses_01J8Z4M2Q0R7T9YV3K6N8P1W2A'}),/id="ay-screen"/);}
}
}
assert.doesNotMatch(render(base),/Uncensored/);
assert.doesNotMatch(render(base),/Uncensored/);
+ const computer = render(base);
+ assert.match(computer, /data-ov-machines[^>]*data-needs="desktop"[^>]*data-name="machine_id"/);
+ assert.doesNotMatch(computer, /Link machine ID/);
+ assert.match(render(base, { machine: 'dev_01J8Z4M2Q0R7T9YV3K6N8P1W2A' }), /data-value="dev_01J8Z4M2Q0R7T9YV3K6N8P1W2A"/);
assert.doesNotMatch(render({...base, actor:{...a, active_task:'tsk_01J8Z4M2Q0R7T9YV3K6N8P1W2A'}, model_switch_enabled:false}),/ay-run-model/);
assert.doesNotMatch(render({...base, actor:{...a, active_task:'tsk_01J8Z4M2Q0R7T9YV3K6N8P1W2A'}, model_switch_enabled:false}),/ay-run-model/);
// On: one row in Now. The label says what it changes, the button is one word, the choices are grouped by who runs
// On: one row in Now. The label says what it changes, the button is one word, the choices are grouped by who runs
// them and named as the chat's model menu names them.
// them and named as the chat's model menu names them.