eyeling 1.24.17 → 1.24.18

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/HANDBOOK.md CHANGED
@@ -3760,6 +3760,8 @@ When a user does want a portable link, the **Copy share link** button creates on
3760
3760
  - edited programs are shared with a compact compressed `?state=...` payload,
3761
3761
  - default option values are omitted from that payload to keep links small.
3762
3762
 
3763
+ If a generated compact share link is still very long, the playground reveals an **Open shortener** option. The threshold is intentionally conservative, and the shortener handoff is explicit rather than automatic: the browser opens a TinyURL creation page with the generated share URL only after the user chooses that option. This avoids silently sending encoded editor content to a third-party service.
3764
+
3763
3765
  This keeps everyday use pleasant while preserving the important tutorial and issue-reporting workflow: a link can still capture the imported resource, the local editable overlay, background-knowledge mode, proof-comments mode, and HTTPS-dereferencing mode.
3764
3766
 
3765
3767
  For compatibility, older `?edit=`, `?program=`, `?url=`, compact `?state=`, and hash-based links are still accepted when opened. The old `/demo` entry point is also kept as a redirect to the canonical `/playground` page.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.24.17",
3
+ "version": "1.24.18",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -722,6 +722,7 @@ async function main() {
722
722
  renderedTabSelected: renderedTab ? renderedTab.getAttribute('aria-selected') === 'true' : false,
723
723
  sourceTabSelected: sourceTab ? sourceTab.getAttribute('aria-selected') === 'true' : false,
724
724
  shareStatus: document.getElementById('share-status') ? String(document.getElementById('share-status').textContent || '') : '',
725
+ shortenerHidden: document.getElementById('open-shortener-btn') ? !!document.getElementById('open-shortener-btn').hidden : true,
725
726
  backgroundStatus: document.getElementById('background-status') ? String(document.getElementById('background-status').textContent || '') : '',
726
727
  href: String(window.location.href || ''),
727
728
  highlighted,
@@ -776,6 +777,19 @@ async function main() {
776
777
  return await evalInPage(`window.__eyelingPlaygroundMakeShareUrl()`);
777
778
  }
778
779
 
780
+ async function makeShareUrlDiagnosticsInPage() {
781
+ return await evalInPage(`(async () => {
782
+ const url = await window.__eyelingPlaygroundMakeShareUrl();
783
+ return {
784
+ url,
785
+ length: url.length,
786
+ threshold: window.__eyelingPlaygroundShareUrlShortenerThreshold,
787
+ needsShortener: window.__eyelingPlaygroundShouldOfferShortener(url),
788
+ shortenerUrl: window.__eyelingPlaygroundMakeShareUrlShortenerUrl(url),
789
+ };
790
+ })()`);
791
+ }
792
+
779
793
  async function loadUrlIntoEditor(url) {
780
794
  const payload = JSON.stringify(String(url));
781
795
  await evalInPage(`(() => {
@@ -922,9 +936,25 @@ ${JSON.stringify(last, null, 2)}`);
922
936
  assert.match(compactShareUrl, /[?&]state=/, 'Expected an on-demand compact state parameter');
923
937
  assert.doesNotMatch(compactShareUrl, /[?&](?:edit|program)=/, 'Expected share link to avoid raw edit/program params');
924
938
  assert.ok(compactShareUrl.length < playgroundUrl.length + encodeURIComponent(outputStringProgram).length, 'Expected compact share URL to be shorter than raw editor URL');
939
+ assert.equal(renderedAgain.shortenerHidden, true, 'Expected ordinary compact share links to keep the shortener option hidden');
940
+ endTest();
941
+
942
+ // 6) Very large edited programs should offer a URL shortener handoff instead of only a huge link.
943
+ beginTest('playground offers a URL shortener option for oversized share links');
944
+ const longShareProgram = Array.from({ length: 1400 }, (_, i) => {
945
+ const n = String(i).padStart(4, '0');
946
+ const token = ((i * 2654435761) >>> 0).toString(36).padStart(7, '0');
947
+ return `:s${n}_${token} :p${token}_${n} "literal-${n}-${token}-${i * 9973}" .`;
948
+ }).join('\n');
949
+ await setProgram(longShareProgram);
950
+ const longShare = await makeShareUrlDiagnosticsInPage();
951
+ assert.ok(longShare.length > longShare.threshold, `Expected test share URL to exceed threshold (${longShare.length} <= ${longShare.threshold})`);
952
+ assert.equal(longShare.needsShortener, true, 'Expected oversized share URL to request a shortener option');
953
+ assert.match(longShare.shortenerUrl, /^https:\/\/tinyurl\.com\/create\.php\?url=/, 'Expected TinyURL create URL handoff');
954
+ assert.ok(longShare.shortenerUrl.includes(encodeURIComponent(longShare.url).slice(0, 40)), 'Expected shortener URL to carry the generated share URL');
925
955
  endTest();
926
956
 
927
- // 6) log:query can produce Turtle; that should stay in plain source output without Markdown tabs.
957
+ // 7) log:query can produce Turtle; that should stay in plain source output without Markdown tabs.
928
958
  beginTest('playground hides markdown tabs for Turtle log:query output');
929
959
  await setProgram(logQueryTurtleProgram);
930
960
  await clickRun();
@@ -944,7 +974,7 @@ ${JSON.stringify(last, null, 2)}`);
944
974
  assert.equal(logQueryTurtle.sourceHidden, false, 'Expected Turtle log:query output to show source directly');
945
975
  endTest();
946
976
 
947
- // 7) URL-loaded examples should auto-load matching examples/input/<stem>.trig and run in RDF/TriG mode.
977
+ // 8) URL-loaded examples should auto-load matching examples/input/<stem>.trig and run in RDF/TriG mode.
948
978
  beginTest('playground auto-loads companion TriG sidecars and uses RDF/TriG mode');
949
979
  await loadUrlIntoEditor('https://raw.githubusercontent.com/eyereasoner/eyeling/refs/heads/main/examples/smoke-arithmetic.n3');
950
980
  const smokeLoaded = await waitForState(
@@ -991,7 +1021,7 @@ ${JSON.stringify(last, null, 2)}`);
991
1021
  assert.equal(smokeRenderedAgain.sourceHidden, true, 'Expected smoke-arithmetic source editor to hide again');
992
1022
  endTest();
993
1023
 
994
- // 8) URL-loaded repository examples should auto-load matching examples/builtin/<stem>.js.
1024
+ // 9) URL-loaded repository examples should auto-load matching examples/builtin/<stem>.js.
995
1025
  beginTest('playground auto-loads a companion example builtin for URL-loaded Sudoku');
996
1026
  await loadUrlIntoEditor('https://raw.githubusercontent.com/eyereasoner/eyeling/refs/heads/main/examples/sudoku.n3');
997
1027
  await waitForState(
@@ -1015,12 +1045,12 @@ ${JSON.stringify(last, null, 2)}`);
1015
1045
  assert.match(sudoku.output, /unique valid Sudoku solution/i, 'Expected Sudoku builtin-backed result');
1016
1046
  endTest();
1017
1047
 
1018
- // 9) Ensure no uncaught runtime exceptions.
1048
+ // 10) Ensure no uncaught runtime exceptions.
1019
1049
  beginTest('playground has no uncaught runtime exceptions');
1020
1050
  assert.equal(exceptions.length, 0, `Uncaught exceptions in playground.html: ${JSON.stringify(exceptions[0] || {})}`);
1021
1051
  endTest();
1022
1052
 
1023
- // 10) Console errors are noisy and often indicate a broken UI.
1053
+ // 11) Console errors are noisy and often indicate a broken UI.
1024
1054
  // (We suppress known noise like /favicon.ico on the server.)
1025
1055
  beginTest('playground has no console errors');
1026
1056
  assert.equal(consoleErrors.length, 0, `Console errors in playground.html: ${JSON.stringify(consoleErrors[0] || {})}`);