arcane-os 0.40.0 → 0.41.0

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.
@@ -518,7 +518,7 @@
518
518
  requestUserMute();
519
519
  } else {
520
520
  pendingUnmute = true;
521
- host.muted = true;
521
+ host.muted = false;
522
522
  if (!settleSuccessfulUnmute()) {
523
523
  synchronizeAIMutedState();
524
524
  continuePendingUnmute();
@@ -567,21 +567,24 @@
567
567
  if (sttBecameUnready) {
568
568
  cancelSTTOperation('runtime-unready');
569
569
  }
570
- if (ttsBecameUnready) {
570
+ if (ttsBecameUnready && ttsRole.state !== 'error') {
571
571
  pendingUnmute = false;
572
- host.muted = true;
573
572
  stopTTSPlayback();
574
573
  }
575
- if (ttsRole.state === 'error' && (pendingUnmute || !host.muted)) {
574
+ if (ttsRole.state === 'error') {
576
575
  pendingUnmute = false;
577
- host.muted = true;
578
- stopTTSPlayback();
579
576
  }
580
577
 
581
- settleSuccessfulUnmute();
578
+ const unmuteSettled = settleSuccessfulUnmute();
582
579
  continuePendingUnmute();
583
580
 
584
581
  synchronizeAIMutedState();
582
+ if (!unmuteSettled
583
+ && previousTTSRole.state !== 'ready'
584
+ && ttsRole.state === 'ready'
585
+ && !host.muted) {
586
+ resumeTTSPlayback();
587
+ }
585
588
  renderControls();
586
589
  renderStatus();
587
590
  }
@@ -720,6 +723,9 @@
720
723
  if (pendingTTSIntent === 'load') {
721
724
  label = 'Cancel voice load';
722
725
  description = 'Cancel loading the selected voice.';
726
+ } else if (!host.muted) {
727
+ label = 'Load voice';
728
+ description = 'Load the selected voice.';
723
729
  }
724
730
  } else if (ttsRole.state === 'error') {
725
731
  disabled = !providerSelected;
@@ -960,7 +966,7 @@
960
966
  return;
961
967
  }
962
968
 
963
- if (host.muted) {
969
+ if (host.muted || ['unloaded', 'error'].includes(ttsRole.state)) {
964
970
  void requestUserUnmute();
965
971
  } else {
966
972
  requestUserMute();
@@ -976,7 +982,7 @@
976
982
  }
977
983
  const wasMuted = host.muted;
978
984
  pendingUnmute = false;
979
- const shouldUnload = !wasMuted
985
+ const shouldUnload = (!wasMuted && is.function(globalThis.ai?.setSpeechMuted))
980
986
  || pendingTTSIntent === 'load'
981
987
  || ['loading', 'ready', 'error'].includes(ttsRole.state);
982
988
  host.muted = true;
@@ -1004,9 +1010,9 @@
1004
1010
  ) {
1005
1011
  return false;
1006
1012
  }
1013
+ host.muted = false;
1007
1014
  if (!is.function(globalThis.ai?.setSpeechMuted)) {
1008
1015
  pendingUnmute = true;
1009
- host.muted = true;
1010
1016
  renderControls();
1011
1017
  renderStatus();
1012
1018
  return false;
@@ -1021,8 +1027,7 @@
1021
1027
  && ['unloaded', 'loading', 'error'].includes(ttsRole.state)
1022
1028
  );
1023
1029
  if (!canRequestLoad) {
1024
- host.muted = true;
1025
- stopTTSPlayback();
1030
+ pendingUnmute = false;
1026
1031
  reportTTSLifecycleError(
1027
1032
  new Error('No selected speech provider is available to load.'),
1028
1033
  'load'
@@ -1038,8 +1043,6 @@
1038
1043
  }
1039
1044
  if (!requested) {
1040
1045
  pendingUnmute = false;
1041
- host.muted = true;
1042
- stopTTSPlayback();
1043
1046
  renderControls();
1044
1047
  renderStatus();
1045
1048
  return false;
@@ -1117,8 +1120,6 @@
1117
1120
  pendingTTSIntent = null;
1118
1121
  if (action === 'load') {
1119
1122
  pendingUnmute = false;
1120
- host.muted = true;
1121
- stopTTSPlayback();
1122
1123
  }
1123
1124
  reportTTSLifecycleError(error, action, operationId);
1124
1125
  return false;
@@ -1211,8 +1212,6 @@
1211
1212
  : 'synthesis';
1212
1213
  const failure = failures[normalizedBoundary];
1213
1214
  const message = visibleErrorMessage(error, failure.message);
1214
- host.muted = true;
1215
- stopTTSPlayback();
1216
1215
  localStatus = {
1217
1216
  message,
1218
1217
  tone: 'error'
@@ -13,7 +13,7 @@ import {APP_CONFIG_NAME, validateAppConfig} from './packager/core.mjs';
13
13
  import {createEventQueue} from './event-queue.mjs';
14
14
  import {
15
15
  applyPwaEntryReferences,inspectImportMapHtml,MANAGED_IMPORT_MAP_ATTRIBUTE,
16
- readWorkspaceAssetVersion,rewriteAssetReferences,versionAssetUrl
16
+ readWorkspaceAssetVersion,resolveAssetReference,rewriteAssetReferences,versionAssetUrl
17
17
  } from './import-map.mjs';
18
18
  import {createPwaArtifacts,selectPwaFiles} from './pwa.mjs';
19
19
 
@@ -658,14 +658,15 @@ async function sourcePwaAssets(routeSet, mappings, signal, resourceUrls, resourc
658
658
  }
659
659
  }
660
660
  }
661
- for (const {url, kind, baseHref, baseKind} of references) {
661
+ for (const reference of references) {
662
+ const {url, kind} = reference;
662
663
  if (kind === 'import' && !/^(?:\.{1,2}\/|\/)/u.test(url)) continue;
663
664
  let target;
664
- let base;
665
665
  try {
666
- base = baseHref ? new URL(baseHref, current.url)
667
- : managedMap || baseKind === 'document' ? documentUrl : current.url;
668
- target = new URL(versionAssetUrl(url, null), base);
666
+ target = resolveAssetReference(
667
+ {...reference, url: versionAssetUrl(url, null)},
668
+ {ownerUrl: current.url, documentUrl, managedMap}
669
+ );
669
670
  } catch {
670
671
  // Non-URL source values retain their authored behavior.
671
672
  continue;
@@ -695,6 +696,11 @@ async function sourcePwaAssets(routeSet, mappings, signal, resourceUrls, resourc
695
696
  return [...selectedUrls].sort();
696
697
  }
697
698
 
699
+ export async function collectSourcePwaAssets({workspaceRoot,appId,signal,onEvent}={}){
700
+ const routes=await sourceRoutes(workspaceRoot,appId,{signal,onEvent});
701
+ return sourcePwaAssets(routes,routes.mappings,signal,new Set(),new Set());
702
+ }
703
+
698
704
  function closeDevelopmentListeners(fileServer, tlsServer) {
699
705
  const closeTls = tlsServer ? new Promise(
700
706
  function closeRawTlsListener(resolve, reject) {
@@ -1236,13 +1242,16 @@ async function startOwnedDevServer({
1236
1242
  .includes(request.headers['sec-fetch-dest']);
1237
1243
  const rewrite=runtimeResource||entryDocument||browserResource||managedMap
1238
1244
  ||resourcePaths.has(target.path);
1239
- const onReference = function observeServedResource({url,kind,baseHref,baseKind}) {
1245
+ const onReference = function observeServedResource(reference) {
1246
+ const {url,kind,baseKind}=reference;
1240
1247
  if (pwaEnabled && kind !== 'fetch'
1241
1248
  && !(kind === 'import' && !/^(?:\.{1,2}\/|\/)/u.test(url))) {
1242
1249
  try {
1243
1250
  const documentUrl = new URL(target.path, 'http://arcane.invalid');
1244
- const base = baseHref ? new URL(baseHref, documentUrl) : documentUrl;
1245
- const resource = new URL(versionAssetUrl(url, null), base);
1251
+ const resource = resolveAssetReference(
1252
+ {...reference,url:versionAssetUrl(url,null)},
1253
+ {ownerUrl:documentUrl}
1254
+ );
1246
1255
  if (resource.origin === documentUrl.origin) {
1247
1256
  pwaResourceUrls.add(`${resource.pathname}${resource.search}`);
1248
1257
  }
@@ -1254,8 +1263,7 @@ async function startOwnedDevServer({
1254
1263
  ||(kind==='import'&&!/^(?:\.{1,2}\/|\/)/u.test(url)))return;
1255
1264
  try{
1256
1265
  const documentUrl=new URL(target.path,'http://arcane.invalid');
1257
- const base=baseHref?new URL(baseHref,documentUrl):documentUrl;
1258
- const resource=new URL(url,base);
1266
+ const resource=resolveAssetReference(reference,{ownerUrl:documentUrl});
1259
1267
  if(resource.origin===documentUrl.origin
1260
1268
  &&/\.(?:m?js|html?|css)$/iu.test(resource.pathname)){
1261
1269
  resourcePaths.add(decodeURIComponent(resource.pathname));
@@ -1281,6 +1281,23 @@ export function rewriteAssetReferences(source,{filePath,version=SDK_VERSION,onRe
1281
1281
  return source;
1282
1282
  }
1283
1283
 
1284
+ export function resolveAssetReference({url,baseHref,baseKind},{ownerUrl,documentUrl=ownerUrl,managedMap=false}){
1285
+ if(baseKind==='component-runtime'&&url.startsWith('./arcane/')){
1286
+ const owner=new URL(ownerUrl);
1287
+ const marker='/arcane/components/';
1288
+ const componentIndex=owner.pathname.lastIndexOf(marker);
1289
+ if(componentIndex>=0){
1290
+ // HTMLImport resolves these component attributes against their
1291
+ // installed runtime, independently of the host document's base.
1292
+ const runtimeRoot=new URL(owner.pathname.slice(0,componentIndex+'/arcane/'.length),owner.origin);
1293
+ return new URL(url.slice('./arcane/'.length),runtimeRoot);
1294
+ }
1295
+ }
1296
+ const base=baseHref?new URL(baseHref,ownerUrl)
1297
+ :managedMap||baseKind==='document'?documentUrl:ownerUrl;
1298
+ return new URL(url,base);
1299
+ }
1300
+
1284
1301
  function registerSpecifier(registry,specifier,target){
1285
1302
  registry.set(specifier,{specifier,target});
1286
1303
  }
@@ -2071,7 +2088,9 @@ function htmlReferenceEdits(source,version,onReference){
2071
2088
  :element.tag==='link'&&relationships.includes('preload')
2072
2089
  &&['script','worker','style'].includes(destination)?destination==='style'?'style':'script':'asset';
2073
2090
  const view=htmlAttributeView(original);
2074
- if(view)reportAssetReference(reportHtmlReference,view.decoded,kind);
2091
+ if(view)reportAssetReference(reportHtmlReference,view.decoded,kind,
2092
+ (name==='href'||name==='src')&&view.decoded.startsWith('./arcane/')
2093
+ ?'component-runtime':undefined);
2075
2094
  const value=versionHtmlAttribute(original,version);
2076
2095
  if(value===original)continue;
2077
2096
  const position=attributes.positions.get(name);
@@ -19,6 +19,7 @@ import {
19
19
  applyPwaEntryReferences,
20
20
  inspectImportMapHtml,
21
21
  readWorkspaceAssetVersion,
22
+ resolveAssetReference,
22
23
  rewriteAssetReferences,
23
24
  versionAssetUrl
24
25
  } from '../import-map.mjs';
@@ -967,17 +968,19 @@ async function packageWithContext(context,options={}){
967
968
  resource={references,...(pwaDocumentSources.has(relative)?{content}:{})};
968
969
  resources.set(relative,resource);
969
970
  }
970
- for(const {url,kind,baseHref,baseKind} of resource.references){
971
+ for(const reference of resource.references){
972
+ const {url,kind,baseHref}=reference;
971
973
  const traversable=kind!=='fetch'&&(kind!=='asset'||/\.css(?:[?#]|$)/iu.test(url))
972
974
  &&(kind!=='import'||/^(?:\.{1,2}\/|\/)/u.test(url));
973
975
  if(!pwaEnabled&&!traversable)continue;
974
976
  if(kind==='import'&&!/^(?:\.{1,2}\/|\/)/u.test(url))continue;
975
977
  try{
976
978
  const ownerUrl=new URL(packageResourceUrl(relative),entryUrl.origin);
977
- const base=baseHref?new URL(baseHref,ownerUrl)
978
- :baseKind==='document'||path.posix.basename(relative)==='arcane.importmap.json'
979
- ?current.documentUrl:ownerUrl;
980
- const target=new URL(url,base);
979
+ const target=resolveAssetReference(reference,{
980
+ ownerUrl,
981
+ documentUrl:current.documentUrl,
982
+ managedMap:path.posix.basename(relative)==='arcane.importmap.json'
983
+ });
981
984
  if(target.origin===entryUrl.origin){
982
985
  const file=decodeURIComponent(target.pathname).replace(/^\//u,'');
983
986
  if(pwaEnabled&&offlineInventory.has(file)){
package/src/toolchain.mjs CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  validateWorkspace
12
12
  } from './workspace.mjs';
13
13
  import {loadArcaneIntegratedProvider} from './integrated-provider-loader.mjs';
14
- import {startDevServer} from './dev-server.mjs';
14
+ import {collectSourcePwaAssets,startDevServer} from './dev-server.mjs';
15
15
  import {applyPwaEntryReferences,generateImportMap,readApplicationTestImportMapContext} from './import-map.mjs';
16
16
  import {createPwaArtifacts} from './pwa.mjs';
17
17
  import {readInstalledSdkLayout} from './sdk-runtime-layout.mjs';
@@ -337,11 +337,21 @@ async function refreshRootApplicationFiles(prepared,inspected,importMap,{signal,
337
337
  ...inspected.files.filter(file=>file!=='index.html'||manifest.include.includes('index.html')),
338
338
  importMap.artifactRelativePath
339
339
  ])];
340
+ const sourceOrigin='http://arcane.invalid';
341
+ const selectedPaths=new Set(files.map(function selectedStaticPath(file){
342
+ return new URL(`/${file.split('/').map(encodeURIComponent).join('/')}`,sourceOrigin).pathname;
343
+ }));
344
+ const assets=installed?.direct&&manifest.pwa?.enabled
345
+ ?(await collectSourcePwaAssets({workspaceRoot,appId,signal,onEvent})).filter(function selectedStaticVariant(value){
346
+ const url=new URL(value,sourceOrigin);
347
+ return url.origin===sourceOrigin&&selectedPaths.has(url.pathname);
348
+ }):[];
340
349
  const pwa=installed?.direct&&manifest.pwa?.enabled?createPwaArtifacts({
341
350
  app:{id:appId,displayName:manifest.displayName,version:manifest.version,entry},
342
351
  sdkVersion:installed.version,
343
352
  pwa:manifest.pwa,
344
353
  files,
354
+ assets,
345
355
  basePath:'/',
346
356
  appBase:'/',
347
357
  installationId:`/apps/${appId}/`,