pymations 0.1.0 → 0.2.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.
@@ -0,0 +1 @@
1
+ {"files":["anim.js","opentype.min.js","katex.min.js","katex.min.css","fonts/KaTeX_AMS-Regular.ttf","fonts/KaTeX_AMS-Regular.woff2","fonts/KaTeX_Caligraphic-Bold.ttf","fonts/KaTeX_Caligraphic-Bold.woff2","fonts/KaTeX_Caligraphic-Regular.ttf","fonts/KaTeX_Caligraphic-Regular.woff2","fonts/KaTeX_Fraktur-Bold.ttf","fonts/KaTeX_Fraktur-Bold.woff2","fonts/KaTeX_Fraktur-Regular.ttf","fonts/KaTeX_Fraktur-Regular.woff2","fonts/KaTeX_Main-Bold.ttf","fonts/KaTeX_Main-Bold.woff2","fonts/KaTeX_Main-BoldItalic.ttf","fonts/KaTeX_Main-BoldItalic.woff2","fonts/KaTeX_Main-Italic.ttf","fonts/KaTeX_Main-Italic.woff2","fonts/KaTeX_Main-Regular.ttf","fonts/KaTeX_Main-Regular.woff2","fonts/KaTeX_Math-BoldItalic.ttf","fonts/KaTeX_Math-BoldItalic.woff2","fonts/KaTeX_Math-Italic.ttf","fonts/KaTeX_Math-Italic.woff2","fonts/KaTeX_SansSerif-Bold.ttf","fonts/KaTeX_SansSerif-Bold.woff2","fonts/KaTeX_SansSerif-Italic.ttf","fonts/KaTeX_SansSerif-Italic.woff2","fonts/KaTeX_SansSerif-Regular.ttf","fonts/KaTeX_SansSerif-Regular.woff2","fonts/KaTeX_Script-Regular.ttf","fonts/KaTeX_Script-Regular.woff2","fonts/KaTeX_Size1-Regular.ttf","fonts/KaTeX_Size1-Regular.woff2","fonts/KaTeX_Size2-Regular.ttf","fonts/KaTeX_Size2-Regular.woff2","fonts/KaTeX_Size3-Regular.ttf","fonts/KaTeX_Size3-Regular.woff2","fonts/KaTeX_Size4-Regular.ttf","fonts/KaTeX_Size4-Regular.woff2","fonts/KaTeX_Typewriter-Regular.ttf","fonts/KaTeX_Typewriter-Regular.woff2","fonts/LiberationSans-Bold.ttf","fonts/LiberationSans-BoldItalic.ttf","fonts/LiberationSans-Italic.ttf","fonts/LiberationSans-Regular.ttf"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pymations",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Maths animations from Python on any web page: the browser half of Pymations, with its animation engine. Runs Manim Community scripts. Python runs in the browser with Pyodide.",
5
5
  "type": "module",
6
6
  "main": "./pymations.js",
package/pyctures.py CHANGED
@@ -1,9 +1,9 @@
1
- # GENERATED by circuitry/scripts/sync-pymations-lib.mjs from src/lib/python-canvas/canvas-runtime-py.ts (version 12).
1
+ # GENERATED by circuitry/scripts/sync-pymations-lib.mjs from src/lib/python-canvas/canvas-runtime-py.ts (version 13).
2
2
  # Do not edit here: change it in Circuitry, then run the script.
3
3
  # Pymations is MIT licensed; see LICENSE.
4
4
  import sys, io, json, time, math, traceback, threading, types
5
5
 
6
- VERSION = "12"
6
+ VERSION = "13"
7
7
  USER_FILE = "main.py"
8
8
 
9
9
  # Fixed handles the frame defines before anything runs.
@@ -31,6 +31,7 @@ class _State:
31
31
  self.sections = {} # name -> ms this frame (measure mode)
32
32
  self.vt = None # virtual seconds while recording, else None
33
33
  self.live = False # frame code has run (draw(), await frame(), an event)
34
+ self.source = "" # the user's file, for modules that map lines (Pymations)
34
35
 
35
36
 
36
37
  # WHICH RUN A THREAD IS WORKING FOR. On the device every frame is its own
@@ -776,6 +777,7 @@ def start(source, info_json):
776
777
  S.thread = threading.get_ident()
777
778
  S.busy = True
778
779
  S.started = time.monotonic()
780
+ S.source = source
779
781
  info = json.loads(info_json)
780
782
  S.measuring = bool(info.get("measure"))
781
783
  # Recording: virtual time from frame 0, and the same random numbers each take.
package/pymations.js CHANGED
@@ -68,13 +68,14 @@ function prop(obj, name) {
68
68
 
69
69
  // ---------------------------------------------------------------- images by relative path
70
70
 
71
- /** A relative path to an image or a sound: no scheme, not absolute, a known extension (pages.IMAGE_PATH). */
72
- const IMAGE_PATH = /^(?![a-z][a-z0-9+.-]*:|\/)(?:\.\/)?[^\s"'<>?#]+\.(png|jpe?g|gif|webp|svg|bmp|avif|mp3|wav|ogg|m4a|aac|flac)$/i
71
+ /** A relative path to a picture, a sound or a font file: no scheme, not absolute, a known extension (pages.IMAGE_PATH). */
72
+ const IMAGE_PATH = /^(?![a-z][a-z0-9+.-]*:|\/)(?:\.\/)?[^\s"'<>?#]+\.(png|jpe?g|gif|webp|svg|bmp|avif|mp3|wav|ogg|m4a|aac|flac|ttf|otf|woff2?)$/i
73
73
  export function isRelativeImagePath(s) { return typeof s === 'string' && s.length < 300 && IMAGE_PATH.test(s) }
74
74
  export const IMAGE_TYPES = {
75
75
  png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif',
76
76
  webp: 'image/webp', svg: 'image/svg+xml', bmp: 'image/bmp', avif: 'image/avif',
77
77
  mp3: 'audio/mpeg', wav: 'audio/wav', ogg: 'audio/ogg', m4a: 'audio/mp4', aac: 'audio/aac', flac: 'audio/flac',
78
+ ttf: 'font/ttf', otf: 'font/otf', woff: 'font/woff', woff2: 'font/woff2',
78
79
  }
79
80
  /** The key an image is packed under: as written, without a leading "./". */
80
81
  const packedKey = (s) => s.replace(/^(\.\/)+/, '')
@@ -438,7 +439,7 @@ function unpackEngine(eng) {
438
439
  window.fetch = (input, init) => {
439
440
  const url = typeof input === 'string' ? input : (input && input.url) || String(input)
440
441
  if (url.startsWith(prefix)) {
441
- const b64 = fonts[decodeURIComponent(url.slice(prefix.length))]
442
+ const b64 = fonts[decodeURIComponent(url.slice(prefix.length).split(/[?#]/)[0])]
442
443
  if (b64 != null) return Promise.resolve(new Response(fromBase64(b64), { headers: { 'Content-Type': 'font/ttf' } }))
443
444
  }
444
445
  return pageFetch(input, init)
@@ -1006,6 +1007,9 @@ class Player {
1006
1007
  if (scene.opt.controls !== false) this.makeBar()
1007
1008
  this.onKey = (e) => this.key(e)
1008
1009
  window.addEventListener('keydown', this.onKey)
1010
+ // The engine's notices (a font or an equation still loading after a
1011
+ // while) and errors from work it finishes later, collected once a second.
1012
+ this.poll = setInterval(() => this.takeErrors(), 1000)
1009
1013
  }
1010
1014
 
1011
1015
  /** A new run. Python's first command resets the engine itself. */
@@ -1045,6 +1049,7 @@ class Player {
1045
1049
  }
1046
1050
 
1047
1051
  destroy() {
1052
+ clearInterval(this.poll)
1048
1053
  this.halt()
1049
1054
  this.observer.disconnect()
1050
1055
  window.removeEventListener('keydown', this.onKey)
@@ -1064,7 +1069,15 @@ class Player {
1064
1069
  }
1065
1070
 
1066
1071
  takeErrors() {
1067
- for (const msg of this.engine.takeErrors ? this.engine.takeErrors() : []) this.report(msg)
1072
+ const e = this.engine
1073
+ if (typeof e.takeNotices === 'function') {
1074
+ for (const n of e.takeNotices()) this.scene.log('info', 'animation: ' + ((n && n.message) || String(n)))
1075
+ }
1076
+ // Strings, or {message, line} with the Python line that made the command.
1077
+ for (const x of e.takeErrors ? e.takeErrors() : []) {
1078
+ if (x && typeof x === 'object') this.report((x.line ? 'line ' + x.line + ': ' : '') + (x.message || String(x)))
1079
+ else this.report(String(x))
1080
+ }
1068
1081
  }
1069
1082
 
1070
1083
  report(msg) {