@ai-ecoverse/slicc-spectrum 2.6.0 → 2.7.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.
package/dist/slicc-ui.js CHANGED
@@ -24607,7 +24607,7 @@ function createEditSessionFile(file) {
24607
24607
  return editSessionFile;
24608
24608
  }
24609
24609
  var instanceId2 = -1;
24610
- var File = class {
24610
+ var File2 = class {
24611
24611
  options;
24612
24612
  workerManager;
24613
24613
  requestOwnerRender;
@@ -35643,6 +35643,7 @@ var SliccComposer = class extends ThemedElement {
35643
35643
  entry: { state: true },
35644
35644
  failure: { state: true },
35645
35645
  reattach: { state: true },
35646
+ recording: { state: true },
35646
35647
  agent: {}
35647
35648
  };
35648
35649
  #drafts = /* @__PURE__ */ new Map();
@@ -35651,6 +35652,7 @@ var SliccComposer = class extends ThemedElement {
35651
35652
  #paths = [];
35652
35653
  #agent = "";
35653
35654
  #next = 1;
35655
+ #session = null;
35654
35656
  #abandon;
35655
35657
  #recognition = null;
35656
35658
  #erase;
@@ -35678,6 +35680,7 @@ var SliccComposer = class extends ThemedElement {
35678
35680
  this.entry = null;
35679
35681
  this.failure = null;
35680
35682
  this.reattach = null;
35683
+ this.recording = null;
35681
35684
  this.agent = "";
35682
35685
  }
35683
35686
  static styles = [
@@ -36223,23 +36226,98 @@ ${this.value}` : text4;
36223
36226
  release(this.attachments.filter((attachment2) => attachment2.id === id));
36224
36227
  this.attachments = this.attachments.filter((attachment2) => attachment2.id !== id);
36225
36228
  }
36226
- async screenshot() {
36227
- const browser = this.model?.browser;
36228
- const tab = browser?.list().find((candidate) => candidate.id === browser.active());
36229
- if (!(browser && tab)) return;
36230
- const url = await browser.screenshot(tab.id);
36229
+ #failAttach(name, error, mimeType = "application/octet-stream") {
36231
36230
  this.attachments = [
36232
36231
  ...this.attachments,
36233
36232
  {
36234
36233
  id: this.#id(),
36235
- name: `${tab.title}.png`,
36236
- kind: "image",
36237
- mimeType: "image/png",
36238
- size: url.length,
36239
- url
36234
+ name,
36235
+ kind: "file",
36236
+ mimeType,
36237
+ size: 0,
36238
+ error
36240
36239
  }
36241
36240
  ];
36242
36241
  }
36242
+ #switchOff(key) {
36243
+ const found = this.model?.switches?.list().find((item2) => item2.key === key);
36244
+ return !!found && !found.on;
36245
+ }
36246
+ #captureError(error, fallback, switchKey) {
36247
+ if (!(error instanceof Error)) return fallback;
36248
+ if (error.name === "AbortError") return "";
36249
+ if (error.name === "NotAllowedError" && switchKey && this.#switchOff(switchKey)) {
36250
+ const labels = {
36251
+ screen: "Screen capture",
36252
+ camera: "Camera",
36253
+ microphone: "Microphone"
36254
+ };
36255
+ return `${labels[switchKey]} is switched off. Turn it on in Settings, Capture and devices.`;
36256
+ }
36257
+ if (error.name === "NotAllowedError") return error.message || fallback;
36258
+ if (error.name === "NotSupportedError")
36259
+ return error.message || "Recording isn\u2019t supported here";
36260
+ return error.message || fallback;
36261
+ }
36262
+ #errorDetail(error) {
36263
+ return error.replace(" is switched off.", " is off.").replace(", Capture and devices.", ".");
36264
+ }
36265
+ async screenshot() {
36266
+ const capture = this.model?.capture;
36267
+ if (capture?.still) {
36268
+ try {
36269
+ const shot = await capture.still();
36270
+ await this.addFiles([new File([shot.blob], shot.name, { type: shot.mimeType })]);
36271
+ } catch (error) {
36272
+ const message = this.#captureError(error, "Screen capture was denied", "screen");
36273
+ if (message) this.#failAttach("screenshot.png", message, "image/png");
36274
+ }
36275
+ return;
36276
+ }
36277
+ const browser = this.model?.browser;
36278
+ const tab = browser?.list().find((candidate) => candidate.id === browser.active());
36279
+ if (!(browser && tab)) {
36280
+ this.#failAttach("screenshot.png", "No screen or browser tab to screenshot", "image/png");
36281
+ return;
36282
+ }
36283
+ try {
36284
+ const url = await browser.screenshot(tab.id);
36285
+ this.attachments = [
36286
+ ...this.attachments,
36287
+ {
36288
+ id: this.#id(),
36289
+ name: `${tab.title}.png`,
36290
+ kind: "image",
36291
+ mimeType: "image/png",
36292
+ size: url.length,
36293
+ url
36294
+ }
36295
+ ];
36296
+ } catch (error) {
36297
+ const message = this.#captureError(error, "Could not take a screenshot");
36298
+ if (message) this.#failAttach("screenshot.png", message, "image/png");
36299
+ }
36300
+ }
36301
+ async record(kind) {
36302
+ const capture = this.model?.capture;
36303
+ if (!capture || this.#session) return;
36304
+ try {
36305
+ const session2 = await capture.record(kind);
36306
+ this.#session = session2;
36307
+ this.recording = kind;
36308
+ const take2 = await session2.finished;
36309
+ this.#session = null;
36310
+ this.recording = null;
36311
+ await this.addFiles([new File([take2.blob], take2.name, { type: take2.mimeType })]);
36312
+ } catch (error) {
36313
+ this.#session = null;
36314
+ this.recording = null;
36315
+ const mic = kind === "audio";
36316
+ const fallback = mic ? "Microphone access was denied" : "Camera access was denied";
36317
+ const message = this.#captureError(error, fallback, mic ? "microphone" : "camera");
36318
+ if (message) this.#failAttach("recording.webm", message, mic ? "audio/webm" : "video/webm");
36319
+ }
36320
+ }
36243
36321
  async attachPath(path2) {
36244
36322
  const text4 = await this.model?.files.read(path2).catch(() => null);
36245
36323
  const name = path2.slice(path2.lastIndexOf("/") + 1);
@@ -36613,6 +36691,8 @@ ${this.value}` : text4;
36613
36691
  if (value === "upload")
36614
36692
  this.renderRoot.querySelector("input[type=file]")?.click();
36615
36693
  else if (value === "screenshot") void this.screenshot();
36694
+ else if (value === "record-audio") void this.record("audio");
36695
+ else if (value === "record-video") void this.record("video");
36616
36696
  else {
36617
36697
  this.openPopup(value);
36618
36698
  void this.updateComplete.then(
@@ -36660,7 +36740,7 @@ ${this.value}` : text4;
36660
36740
  return b`<span slot="thumbnail" class="thumb" ?data-error=${!!attachment2.error} aria-hidden="true">${icon}</span>`;
36661
36741
  }
36662
36742
  #attachment(attachment2) {
36663
- const detail = attachment2.error ?? (attachment2.kind === "secret" ? "Secret" : size3(attachment2.size));
36743
+ const detail = attachment2.error ? this.#errorDetail(attachment2.error) : attachment2.kind === "secret" ? "Secret" : size3(attachment2.size);
36664
36744
  return b`<swc-upload-attachment
36665
36745
  slot="attachment"
36666
36746
  type="card"
@@ -36668,12 +36748,12 @@ ${this.value}` : text4;
36668
36748
  data-id=${attachment2.id}
36669
36749
  data-kind=${attachment2.kind}
36670
36750
  ?data-error=${!!attachment2.error}
36671
- title=${attachment2.path ?? attachment2.name}
36751
+ title=${attachment2.error ?? attachment2.path ?? attachment2.name}
36672
36752
  @swc-upload-attachment-dismiss=${() => this.detach(attachment2.id)}
36673
36753
  >
36674
36754
  ${this.#thumbnail(attachment2)}
36675
36755
  <span slot="title">${attachment2.name}</span>
36676
- <span slot="subtitle" class=${attachment2.error ? "failed" : ""}>${detail}</span>
36756
+ <span slot="subtitle" class=${attachment2.error ? "failed" : ""} title=${attachment2.error ?? A}>${detail}</span>
36677
36757
  </swc-upload-attachment>`;
36678
36758
  }
36679
36759
  #popup() {
@@ -36946,7 +37026,9 @@ ${this.value}` : text4;
36946
37026
  <swc-popover placement="top-start" accessible-label="Add to the message" @keydown=${this.#menuKey} @swc-after-open=${() => this.renderRoot.querySelector("sp-menu")?.focus()}>
36947
37027
  <sp-menu label="Add to the message" @change=${this.#add}>
36948
37028
  <sp-menu-item value="upload">Upload from this computer</sp-menu-item>
36949
- <sp-menu-item value="screenshot">Take a screenshot of the browser tab</sp-menu-item>
37029
+ <sp-menu-item value="screenshot" ?disabled=${!!this.recording}>Take a screenshot</sp-menu-item>
37030
+ ${this.model?.capture ? b`<sp-menu-item value="record-audio" data-action="record-audio" ?disabled=${!!this.recording}>Record audio</sp-menu-item>` : A}
37031
+ ${this.model?.capture?.kinds?.includes("video") ? b`<sp-menu-item value="record-video" data-action="record-video" ?disabled=${!!this.recording}>Record video</sp-menu-item>` : A}
36950
37032
  <sp-menu-item value="file">Attach a file from SLICC</sp-menu-item>
36951
37033
  <sp-menu-item value="secret">Share a secret</sp-menu-item>
36952
37034
  ${talk ? b`<sp-menu-divider></sp-menu-divider>
@@ -54181,7 +54263,7 @@ var SliccLive = class extends ModelElement {
54181
54263
  // src/app/lucide.ts
54182
54264
  var library = null;
54183
54265
  function load2() {
54184
- library ??= import("./chunk-NMHCUT6N.js");
54266
+ library ??= import("./chunk-3MP6GPRK.js");
54185
54267
  return library;
54186
54268
  }
54187
54269
  function pascal(name) {
@@ -55770,7 +55852,7 @@ var SliccSettings = class extends ThemedElement {
55770
55852
  (item2) => b`<sp-switch .checked=${l(item2.on)} data-id=${item2.key} @change=${(event2) => port.set(item2.key, this.#checked(event2))}>${item2.label}</sp-switch>`
55771
55853
  )}
55772
55854
  </div>
55773
- <p class="hint">Programs can capture only what’s on here. Turning one off stops what’s live under it.</p>
55855
+ <p class="hint">Programs and linked devices can use only what’s on here. Turning one off stops what’s live under it.</p>
55774
55856
  </div>
55775
55857
  </div>
55776
55858
  </div>`;
@@ -58427,7 +58509,7 @@ var SliccCodeView = class extends PierreView {
58427
58509
  }
58428
58510
  draw(container) {
58429
58511
  if (this.#contents === null) return this.empty(container, "Nothing to show");
58430
- const viewer = new File(this.options());
58512
+ const viewer = new File2(this.options());
58431
58513
  viewer.render({
58432
58514
  file: { name: this.path, contents: this.#contents },
58433
58515
  containerWrapper: container