typebulb 0.13.1 → 0.13.2
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.
- package/README.md +1 -1
- package/dist/agents/claude/client.js +17 -2
- package/dist/agents/claude/styles.css +4 -3
- package/dist/index.js +163 -148
- package/dist/render.js +17 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -262,7 +262,7 @@ The host owns a bulb's **width**; you own its **height**.
|
|
|
262
262
|
|
|
263
263
|
**Width is the host's.** Standalone, a bulb fills its browser window; in the agent mirror, an embed fits the conversation column by default, with a per-embed *spread* toggle to the full transcript width — and a cap so a tall embed doesn't run away down the transcript. Don't set a width or guess how much room you'll get.
|
|
264
264
|
|
|
265
|
-
**Height follows your content.** Prose, a form, a chart flow to their natural height
|
|
265
|
+
**Height follows your content.** Set a height that adapts — content-driven or viewport-filling — never a fixed pixel value, which neither grows to fill a broken-out window nor shrinks to its content. Prose, a form, a chart flow to their natural height: set none. A full-bleed surface with no natural height of its own gets `height: 100dvh` **and** a pixel floor like `min-height: 420px`. Both are needed — `100dvh` fills its own window if the bulb is broken out, and the floor holds a definite band when embedded. Without the floor a bare `100dvh` collapses to zero embedded, because the mirror sizes an embed to its content height and `100dvh` gives it nothing to measure against.
|
|
266
266
|
|
|
267
267
|
**When embedded, keep vertical space on the root in `padding`, not `margin`.** The mirror measures an embed by `document.body.scrollHeight`, and the runtime makes `body` a block formatting context so a root child's vertical margin (yours, or a UA default like `<h1>`'s) is contained rather than escaping the measurement — so you no longer have to get this exactly right. It's still cleaner to keep the horizontal `auto` for centering and move the vertical space to padding:
|
|
268
268
|
|
|
@@ -968,6 +968,7 @@ ${iwn}
|
|
|
968
968
|
|
|
969
969
|
<script type="module">
|
|
970
970
|
${Q(a)}
|
|
971
|
+
${B?"globalThis.__tbEntryRan = true; /* load-failure backstop \u2014 see embedProtocol */":""}
|
|
971
972
|
<\/script>
|
|
972
973
|
</body>
|
|
973
974
|
</html>`}var iwn=`<script>
|
|
@@ -1019,17 +1020,25 @@ ${Q(a)}
|
|
|
1019
1020
|
};
|
|
1020
1021
|
var update = function () { if (!raf) raf = requestAnimationFrame(apply); };
|
|
1021
1022
|
de.style.overflow = 'hidden';
|
|
1023
|
+
var errorPosted = false;
|
|
1024
|
+
var postError = function (message) { errorPosted = true; post({ __typebulbEmbed: true, kind: 'error', message: String(message) }); };
|
|
1025
|
+
// Capture phase: a resource/module load failure (a <script>/<link>/<img> that 404s) does NOT
|
|
1026
|
+
// bubble, so it reaches window only here, carrying the failing element as e.target (no message).
|
|
1027
|
+
window.addEventListener('error', function (e) {
|
|
1028
|
+
var t = e && e.target;
|
|
1029
|
+
if (t && t !== window && (t.src || t.href)) postError('Failed to load ' + (t.src || t.href));
|
|
1030
|
+
}, true);
|
|
1022
1031
|
window.addEventListener('error', function (e) {
|
|
1023
1032
|
var m = String((e && e.message) || (e && e.error) || 'Error');
|
|
1024
1033
|
// The benign "ResizeObserver loop completed\u2026" notice surfaces as a window error in
|
|
1025
1034
|
// some browsers; it's not a bulb fault, so don't forward it to the host as one.
|
|
1026
1035
|
if (m.indexOf('ResizeObserver') !== -1) return;
|
|
1027
|
-
|
|
1036
|
+
postError(m);
|
|
1028
1037
|
});
|
|
1029
1038
|
window.addEventListener('unhandledrejection', function (e) {
|
|
1030
1039
|
var r = e && e.reason;
|
|
1031
1040
|
var msg = r && r.message ? r.message : (r == null ? 'Unhandled rejection' : r);
|
|
1032
|
-
|
|
1041
|
+
postError(msg);
|
|
1033
1042
|
});
|
|
1034
1043
|
// Run at load (module mounted \u2192 real content, not the empty pre-mount body that would
|
|
1035
1044
|
// collapse the frame and snap back). Observe BOTH body (content growth) and the root
|
|
@@ -1039,6 +1048,12 @@ ${Q(a)}
|
|
|
1039
1048
|
if (window.ResizeObserver) {
|
|
1040
1049
|
try { var ro = new ResizeObserver(update); ro.observe(document.body); ro.observe(de); } catch (e) {}
|
|
1041
1050
|
}
|
|
1051
|
+
// Load-failure backstop: if the entry module never evaluated \u2014 its import graph failed to
|
|
1052
|
+
// resolve (a 404'd dependency, which throws nothing catchable) \u2014 surface it. A module that
|
|
1053
|
+
// only renders asynchronously still evaluates its top level, so this won't fire on a slow bulb.
|
|
1054
|
+
if (!window.__tbEntryRan && !errorPosted) {
|
|
1055
|
+
postError('Bulb failed to load: a module or dependency could not be fetched (see the browser console for the failing URL).');
|
|
1056
|
+
}
|
|
1042
1057
|
});
|
|
1043
1058
|
})();
|
|
1044
1059
|
<\/script>`;function swn(s){let o={};for(let[a,d]of Object.entries(s))o[a]=d.startsWith("https://")?"/proxy/"+d:d;return o}var Y$="https://esm.sh",X$="https://cdn.jsdelivr.net/npm/",zke="https://data.jsdelivr.com/v1/package/npm/";function HEt(s){let o=(s||"").replace(/^\/+/,"").replace(/\/+$/,"");return o?o.split("/"):[]}var Pf=class s{constructor(o,a,d){let g=typeof o=="string"?s.parse(o):o;this.name=g.name,this.version=qM(a??g.version),this.subpath=qM(d??g.subpath)}static parse(o){let a=HEt(o||"");if(!a.length)return new s({name:""});if(a[0].startsWith("@")){let g=a[0],[m,v]=QEt(a[1]??""),A=qM(a.slice(2).join("/"));return new s({name:`${g}/${m}`,version:v,subpath:A})}else{let[g,m]=QEt(a[0]),v=qM(a.slice(1).join("/"));return new s({name:g,version:m,subpath:v})}}static fromUrl(o){try{let a=new URL(o),d=new URL(Y$).host,g=new URL(X$).host;if(a.host===d){let m=HEt(a.pathname.replace(/^\/v\d+\//,"/"));if(!m.length)return;let v=m[0].startsWith("@")?`${m[0]}/${m[1]??""}`:m[0];return s.parse(v)}if(a.host===g){let m=a.pathname.split("/npm/")[1];if(!m)return;let v=m.split("/")[0]||"";return s.parse(v)}return}catch{return}}static versionFromUrl(o){return s.fromUrl(o)?.version}format(){let o=this.version?`${this.name}@${this.version}`:this.name;return this.subpath?`${o}/${this.subpath}`:o}root(){return this.name}static rootOf(o){return s.parse(o).name}withVersion(o){return new s({name:this.name,version:qM(o),subpath:this.subpath})}withPreferredVersion(o,a){let d=o||a;return d?this.withVersion(d):this}static isBare(o){if(!o||o.startsWith(".")||o.startsWith("/"))return!1;let a=o.toLowerCase();return!a.startsWith("http://")&&!a.startsWith("https://")}},qM=s=>s&&s.length?s:void 0,QEt=s=>{let o=s.indexOf("@");return o<0?[s,void 0]:[s.slice(0,o),qM(s.slice(o+1))]};async function gg(s){try{return await s()}catch{return}}var V$=class{constructor(o,a){this.cache=o,this.http=a,this.esmHost=Y$,this.jsDelivrBase=X$,this.jsDelivrMeta=zke,this.pinMs=1e4,this.versionsIndexMs=1440*60*1e3,this.metaTtlMs=10080*60*1e3,this.pinCache=new Map}normalizeRelative(o){let a=o||"";return a.startsWith("./")?a.slice(2):a.replace(/^\/+/,"")}ensureLeadingDotSlash(o){return o.startsWith("./")?o:`./${o}`}baseDir(o){let a=typeof o=="string"?new Pf(o):o;return`${this.jsDelivrBase}${a.name}${a.version?`@${a.version}`:""}/`}file(o,a){return new URL(this.normalizeRelative(a),this.baseDir(o)).toString()}packageJson(o){return this.file(o,"package.json")}buildEsmUrl(o,a={}){let{target:d="es2022",bundle:g=!1,external:m}=a,v=new URLSearchParams({target:d});return g&&v.append("bundle",""),m?.length&&v.append("external",m.join(",")),`${this.esmHost}/${o}?${v.toString()}`}async pinEsmUrl(o,a="es2022"){let d=this.buildEsmUrl(o,{target:a}),g=await gg(()=>this.http.head(d));return g?.ok?g.url||d:void 0}async resolveExactVersion(o){let a=Date.now(),d=this.pinCache.get(o);if(d&&a-d.ts<this.pinMs)return d.value;let g=await this.tryResolveFromUrls([this.buildEsmUrl(o),`${this.esmHost}/${o}`]);return this.pinCache.set(o,{value:g,ts:a}),g}async tryResolveFromUrls(o){for(let a of o){let d=await gg(()=>this.http.head(a)),g=this.parseVersionFromUrl(d?.url||a);if(g)return g}}async fetchVersionsIndex(o){if(await this.cache.isNegative(o))return;let a=await this.cache.getIndex(o);if(a&&Date.now()-a.updatedAt<this.versionsIndexMs)return{versions:a.versions,distTags:a.distTags};let d=await gg(()=>this.http.getJson(`${this.jsDelivrMeta}${encodeURIComponent(o)}`));if(!d?.versions?.length){await this.cache.recordNegative(o);return}await this.cache.clearNegative(o);let g=d.distTags&&Object.keys(d.distTags).length?d.distTags:void 0;return await this.cache.setIndex(o,d.versions,g),d}parseVersionFromUrl(o){let a=Pf.fromUrl(o)?.version;return a&&/\d+\.\d+\.\d+/.test(a)?a:void 0}async fetchPackageMeta(o,a){let d=await this.cache.getMeta(o,a);if(d&&Date.now()-d.updatedAt<this.metaTtlMs){let{dependencies:B,peerDependencies:L,peerDependenciesMeta:R}=d;return{name:o,version:a,dependencies:B,peerDependencies:L,peerDependenciesMeta:R}}let g=this.packageJson(new Pf(`${o}@${a}`)),m=await gg(()=>this.http.getJson(g));if(!m)return;let v=B=>B&&Object.keys(B).length?B:void 0,A=v(m.dependencies),I=v(m.peerDependencies),T=v(m.peerDependenciesMeta);return await this.cache.setMeta(o,a,A,I,T),{name:o,version:a,dependencies:A,peerDependencies:I,peerDependenciesMeta:T}}};var GEt=s=>s.startsWith("@types/"),Z$=s=>Object.keys(s?.peerDependencies||{}).filter(o=>!GEt(o)),Kke=s=>Object.keys(s?.dependencies||{}).filter(o=>!GEt(o)),own=s=>Z$(s).filter(o=>!s?.peerDependenciesMeta?.[o]?.optional),eH=class{constructor(o){this.cdn=o}async resolve(o,a){let d=await this.fetchMeta(o),{allRoots:g,autoAddedPeers:m}=await this.expandWithPeers(d,a),v=this.computeFlags(g);return{allRoots:g,flags:v,autoAddedPeers:m}}async fetchMeta(o){return Promise.all(o.map(async({name:a,version:d})=>({name:a,version:d,meta:await this.cdn.fetchPackageMeta(a,d)})))}async expandWithPeers(o,a){let d=new Map(o.map(m=>[m.name,m])),g=[];for(let m of o)for(let v of own(m.meta))!d.has(v)&&!g.some(A=>A.name===v)&&g.push({name:v,requiredBy:m.name});for(let{name:m,requiredBy:v}of g)try{let A=await a(m),I=await this.cdn.fetchPackageMeta(m,A);d.set(m,{name:m,version:A,meta:I})}catch(A){console.warn(`[typebulb] Failed to resolve peer "${m}" for "${v}":`,A)}return{allRoots:[...d.values()],autoAddedPeers:g.filter(m=>d.has(m.name))}}computeFlags(o){let a=new Set(o.flatMap(m=>Z$(m.meta))),d=new Map;for(let m of o)for(let v of Kke(m.meta))d.set(v,(d.get(v)||0)+1);let g=new Set([...d.entries()].filter(([,m])=>m>=2).map(([m])=>m));return new Map(o.map(m=>[m.name,{isPeerRoot:a.has(m.name),hasPeers:Z$(m.meta).length>0,isSharedDep:g.has(m.name)}]))}};var tH=class{constructor(o,a,d){this.cache=o,this.cdn=a,this.semver=d}selectVersionFromIndex(o,a,d){return this.semver.selectBestVersion(o,{range:a,distTags:d})}async learnExactVersion(o){let a=await gg(()=>this.cdn.fetchVersionsIndex(o));if(a?.versions?.length){let d=this.semver.selectBestVersion(a.versions,{distTags:a.distTags});if(d)return d}return this.cdn.resolveExactVersion(o)}async resolveExactForRoot(o,a){if(!a)return this.learnExactVersion(o);let d=await this.cache.getPinnedExact(o,a);if(d){if(this.semver.isExactVersion(d))return d;console.debug("[typebulb] cached version for",o,"is not exact (",d,"); re-resolving from registry")}let g=await gg(()=>this.cdn.fetchVersionsIndex(o));if(g?.versions?.length){let v=this.selectVersionFromIndex(g.versions,a,g.distTags);if(v){if(this.semver.isExactVersion(v))return await this.cache.setPinnedExact(o,a,v),v}else{console.debug("[typebulb] refreshing version cache for",o,"(",a,"not in cached set \u2014 likely a new release)"),await this.cache.invalidateVersionsCache(o);let A=await gg(()=>this.cdn.fetchVersionsIndex(o));if(A?.versions?.length){let I=this.selectVersionFromIndex(A.versions,a,A.distTags);if(I&&this.semver.isExactVersion(I))return await this.cache.setPinnedExact(o,a,I),I}}}let m=await this.cdn.resolveExactVersion(`${o}@${a}`);if(m&&this.semver.isExactVersion(m))return await this.cache.setPinnedExact(o,a,m),m}async effectivePackage(o,a){let d=new Pf(o),g=d.root(),m=a[g],v=m?await gg(()=>this.cache.getPinnedExact(g,m))??await gg(()=>this.resolveExactForRoot(g,m)):void 0;return{effectivePackage:v?d.withVersion(v).format():o,root:g,range:m,pinned:v}}};var qEt;(function(s){s[s.Static=1]="Static",s[s.Dynamic=2]="Dynamic",s[s.ImportMeta=3]="ImportMeta",s[s.StaticSourcePhase=4]="StaticSourcePhase",s[s.DynamicSourcePhase=5]="DynamicSourcePhase",s[s.StaticDeferPhase=6]="StaticDeferPhase",s[s.DynamicDeferPhase=7]="DynamicDeferPhase"})(qEt||(qEt={}));var cwn=new Uint8Array(new Uint16Array([1]).buffer)[0]===1;function Wke(s,o="@"){if(!Au)return Yke.then((()=>Wke(s)));let a=s.length+1,d=(Au.__heap_base.value||Au.__heap_base)+4*a-Au.memory.buffer.byteLength;d>0&&Au.memory.grow(Math.ceil(d/65536));let g=Au.sa(a-1);if((cwn?awn:uwn)(s,new Uint16Array(Au.memory.buffer,g,a)),!Au.parse())throw Object.assign(new Error(`Parse error ${o}:${s.slice(0,Au.e()).split(`
|
|
@@ -272,9 +272,10 @@ body {
|
|
|
272
272
|
color: var(--accent); text-decoration: none; cursor: pointer;
|
|
273
273
|
}
|
|
274
274
|
.server-name:hover { text-decoration: underline; }
|
|
275
|
-
/* Port
|
|
276
|
-
|
|
277
|
-
|
|
275
|
+
/* Port links to the running server (localhost:<port>) — accent like the name, so a running
|
|
276
|
+
bulb's clickable :port reads as live against a stopped bulb's muted last-run time. */
|
|
277
|
+
.server-port { color: var(--accent); font-size: .76rem; font-variant-numeric: tabular-nums; text-decoration: none; text-align: right; margin-right: .4rem; }
|
|
278
|
+
a.server-port:hover { text-decoration: underline; }
|
|
278
279
|
/* Logs reads like a link (flips to this server's console), not a button. */
|
|
279
280
|
.server-logs { color: var(--muted); font-size: .76rem; text-decoration: none; cursor: pointer; margin-left: .5rem; }
|
|
280
281
|
.server-logs:hover, .server-logs.on { color: var(--accent); text-decoration: underline; }
|