@gramlot/gramlot-examples 0.3.5 → 0.3.6

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 CHANGED
@@ -12,7 +12,7 @@ npm install @gramlot/gramlot-examples
12
12
  [![PyPI](https://img.shields.io/pypi/v/gramlot-examples)](https://pypi.org/project/gramlot-examples/)
13
13
  [![npm](https://img.shields.io/npm/v/@gramlot/gramlot-examples)](https://www.npmjs.com/package/@gramlot/gramlot-examples)
14
14
 
15
- Current release: 0.3.5.
15
+ Current release: 0.3.6.
16
16
 
17
17
  [Documentation and learning sequence](https://gramlot-examples.readthedocs.io/en/latest/)
18
18
  include every lesson, its Python and JavaScript source, and the gallery guide.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramlot/gramlot-examples",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Gramlot example pages, the gallery page and its catalogue.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -6879,11 +6879,101 @@ Please report this to https://github.com/markedjs/marked.`, e) {
6879
6879
  element.innerHTML = purifier.sanitize(markdown.parse(element.textContent), { USE_PROFILES: { html: true } });
6880
6880
  }
6881
6881
 
6882
+ // src/gramlot_examples/gallery/browser/gallery-content.js
6883
+ var pending = /* @__PURE__ */ new WeakMap();
6884
+ function mountGalleryContent(renderer, root) {
6885
+ const document2 = renderer.destination.ownerDocument;
6886
+ const byId = (id) => document2.getElementById(id);
6887
+ const node = (element) => renderer.elements.get(element).node;
6888
+ const entries2 = /* @__PURE__ */ new Map();
6889
+ let disposed = false;
6890
+ function format({ key, view }) {
6891
+ if (view === "description") {
6892
+ const readme = byId(`readme-${key}`);
6893
+ readme.textContent = node(readme).value;
6894
+ renderReadme(readme);
6895
+ return;
6896
+ }
6897
+ const code = byId(`code-${key}`);
6898
+ code.textContent = node(code).value;
6899
+ highlightCode(code);
6900
+ const logic = byId(`logic-${key}`);
6901
+ if (logic) {
6902
+ const source = logicSource(node(logic).value);
6903
+ node(byId(`logic-panel-${key}`)).setAttr({ hidden: source === null });
6904
+ logic.textContent = source ?? "";
6905
+ if (source !== null) highlightCode(logic);
6906
+ }
6907
+ }
6908
+ async function load(entry) {
6909
+ if (entry.loading) return;
6910
+ entry.loading = true;
6911
+ const targetId = entry.view === "code" ? `code-tabs-${entry.key}` : entry.element.id;
6912
+ const target = node(byId(targetId));
6913
+ const current = () => {
6914
+ const element = byId(targetId);
6915
+ return element && renderer.elements.get(element)?.node === target ? element : null;
6916
+ };
6917
+ let request = pending.get(target);
6918
+ if (!request) {
6919
+ request = renderer.binding.gramlot.src.remoteSource(target, "lesson", { key: entry.key, view: entry.view });
6920
+ pending.set(target, request);
6921
+ }
6922
+ try {
6923
+ const mounted = await request;
6924
+ if (disposed || !current() || !mounted) return;
6925
+ format(entry);
6926
+ observer.unobserve(entry.element);
6927
+ entries2.delete(entry.element);
6928
+ } catch {
6929
+ if (disposed || !current()) return;
6930
+ const status = current().querySelector('[role="status"]');
6931
+ node(status).setValue("Could not load this content. Close and reopen the panel to retry.");
6932
+ } finally {
6933
+ if (pending.get(target) === request) pending.delete(target);
6934
+ entry.loading = false;
6935
+ }
6936
+ }
6937
+ const observer = new document2.defaultView.IntersectionObserver((records) => {
6938
+ for (const record of records) {
6939
+ const entry = entries2.get(record.target);
6940
+ if (!entry) continue;
6941
+ const visible = record.isIntersecting && record.intersectionRect.width > 0 && record.intersectionRect.height > 0;
6942
+ if (visible && !entry.visible) void load(entry);
6943
+ entry.visible = visible;
6944
+ }
6945
+ });
6946
+ const dispose = () => {
6947
+ disposed = true;
6948
+ observer.disconnect();
6949
+ entries2.clear();
6950
+ };
6951
+ renderer.onDispose(root, dispose);
6952
+ return {
6953
+ observe(key) {
6954
+ for (const [view, id, loaded] of [
6955
+ ["code", `codepane-${key}`, `code-${key}`],
6956
+ ["description", `description-${key}`, `readme-${key}`]
6957
+ ]) {
6958
+ const element = byId(id);
6959
+ const entry = { key, view, element, visible: false, loading: false };
6960
+ if (byId(loaded)) format(entry);
6961
+ else if (!entries2.has(element)) {
6962
+ entries2.set(element, entry);
6963
+ observer.observe(element);
6964
+ }
6965
+ }
6966
+ },
6967
+ dispose
6968
+ };
6969
+ }
6970
+
6882
6971
  // src/gramlot_examples/gallery/browser/gallery.js
6883
6972
  function mountGallery(renderer, root, inspector) {
6884
6973
  const state = root.builder.data.getItem("galleryIndex");
6885
6974
  const settings = mountGallerySettings(renderer, root, state);
6886
6975
  let guide = null;
6976
+ const content = mountGalleryContent(renderer, root);
6887
6977
  const mobile = mountGalleryMobile(renderer, root, inspector, state, (visible) => {
6888
6978
  if (!visible) guide?.pause();
6889
6979
  });
@@ -6891,19 +6981,14 @@ Please report this to https://github.com/markedjs/marked.`, e) {
6891
6981
  inspector.update(entries2, active);
6892
6982
  mobile.update(entries2, active);
6893
6983
  guide?.update(entries2, active);
6894
- }, (entry, { byId, node, set, listen }) => {
6984
+ }, (entry, { byId }) => {
6895
6985
  const { key } = entry;
6896
6986
  if (key === "l04" && !guide) guide = mountGuidedDemo(renderer, root);
6897
- renderReadme(byId(`readme-${key}`));
6898
- if (entry.panel.classList.contains("index-category")) return;
6899
- highlightCode(byId(`code-${key}`));
6900
- const logic = byId(`logic-${key}`);
6901
- if (logic) {
6902
- const source = logicSource(node(logic).value);
6903
- set(byId(`logic-panel-${key}`), { hidden: source === null });
6904
- logic.textContent = source ?? "";
6905
- if (source !== null) highlightCode(logic);
6987
+ if (entry.panel.classList.contains("index-category")) {
6988
+ renderReadme(byId(`readme-${key}`));
6989
+ return;
6906
6990
  }
6991
+ content.observe(entry.key);
6907
6992
  }, {
6908
6993
  containerLayout: true,
6909
6994
  state,
@@ -6919,20 +7004,22 @@ Please report this to https://github.com/markedjs/marked.`, e) {
6919
7004
  hidden: true,
6920
7005
  class: category ? "index-category" : "gallery-panel"
6921
7006
  });
6922
- const code = category ? null : source("gallery-code-pages").tabContainer({
7007
+ const code = category ? null : source("gallery-code-pages").div({
6923
7008
  id: `codepane-${entry.key}`,
6924
- class: "gallery-codepane gallery-source-tabs",
7009
+ class: "gallery-codepane",
6925
7010
  hidden: true,
7011
+ height: "100%"
7012
+ });
7013
+ if (code) code.tabContainer({
7014
+ id: `code-tabs-${entry.key}`,
7015
+ class: "gallery-source-tabs",
6926
7016
  height: "100%",
6927
7017
  tabMode: "underline"
6928
- });
7018
+ }).contentPane({ title: "Code" }).p("Loading code\u2026", { role: "status" });
6929
7019
  try {
6930
7020
  const runtime = renderer.binding.gramlot;
6931
- const results = await Promise.all([
6932
- runtime.src.remoteSource(node(`panel-${entry.key}`), "lesson", { key: entry.key }),
6933
- ...code ? [runtime.src.remoteSource(node(`codepane-${entry.key}`), "lesson", { key: entry.key, view: "code" })] : []
6934
- ]);
6935
- if (results.some((mounted) => !mounted)) throw new Error("Lesson loading was cancelled");
7021
+ const mounted = await runtime.src.remoteSource(node(`panel-${entry.key}`), "lesson", { key: entry.key });
7022
+ if (!mounted) throw new Error("Lesson loading was cancelled");
6936
7023
  } catch (error) {
6937
7024
  for (const target of [pane, code]) if (target?.parentBag) target.parentBag.popNode(target.label);
6938
7025
  throw error;
@@ -6945,6 +7032,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
6945
7032
  });
6946
7033
  return { dispose() {
6947
7034
  guide?.dispose();
7035
+ content.dispose();
6948
7036
  settings.dispose();
6949
7037
  index.dispose();
6950
7038
  mobile.dispose();
@@ -7550,11 +7638,11 @@ Please report this to https://github.com/markedjs/marked.`, e) {
7550
7638
  this.error = error.message;
7551
7639
  }
7552
7640
  if (message.type === "result") {
7553
- const pending = this.#pending.get(message.id);
7554
- if (pending) {
7641
+ const pending2 = this.#pending.get(message.id);
7642
+ if (pending2) {
7555
7643
  this.#pending.delete(message.id);
7556
- if (message.ok && !this.error) pending.resolve(message);
7557
- else pending.reject(new Error(message.error || this.error));
7644
+ if (message.ok && !this.error) pending2.resolve(message);
7645
+ else pending2.reject(new Error(message.error || this.error));
7558
7646
  }
7559
7647
  }
7560
7648
  this.#notify();
@@ -7611,7 +7699,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
7611
7699
  this.#port.removeEventListener("message", this.#receive);
7612
7700
  this.#port.close();
7613
7701
  this.state = "disposed";
7614
- for (const pending of this.#pending.values()) pending.reject(new Error("Inspected page disconnected"));
7702
+ for (const pending2 of this.#pending.values()) pending2.reject(new Error("Inspected page disconnected"));
7615
7703
  this.#pending.clear();
7616
7704
  this.#notify();
7617
7705
  }
@@ -7690,9 +7778,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
7690
7778
  _subscribe() {
7691
7779
  if (this._store) this._store.subscribe(this._subId, { any: () => {
7692
7780
  if (this._pendingRender) return;
7693
- const pending = this._pendingRender = {};
7781
+ const pending2 = this._pendingRender = {};
7694
7782
  queueMicrotask(() => {
7695
- if (this._pendingRender === pending && this.isConnected) this._render();
7783
+ if (this._pendingRender === pending2 && this.isConnected) this._render();
7696
7784
  });
7697
7785
  } });
7698
7786
  }
@@ -102,6 +102,7 @@ export class GalleryPage extends IndexPage {
102
102
  lesson(root, {key, view = 'example'}) {
103
103
  const item = this.galleryItem(key);
104
104
  if (view === 'code' && !item.examples) this.codePanel(root, item);
105
+ else if (view === 'description' && !item.examples) root.div(item.readme, {id: `readme-${key}`});
105
106
  else if (view === 'example') {
106
107
  if (item.examples) this.groupPanel(root, item);
107
108
  else this.panel(root, item);
@@ -124,8 +125,8 @@ export class GalleryPage extends IndexPage {
124
125
  guidedDemo(layout);
125
126
  }
126
127
  stage.iframe({title: `${example.title} preview`, name: `example-${key}`, id: `frame-${key}`});
127
- preview.contentPane({title: 'Explanation', pageName: 'explanation', class: 'gallery-lesson', overflow: 'auto'})
128
- .div(example.readme, {id: `readme-${key}`});
128
+ preview.contentPane({title: 'Explanation', pageName: 'explanation', class: 'gallery-lesson', overflow: 'auto', id: `description-${key}`})
129
+ .p('Loading explanation…', {role: 'status'});
129
130
  }
130
131
  codePanel(parent, example) {
131
132
  const key = example.key;