eyeling 1.24.16 → 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.16",
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');
925
940
  endTest();
926
941
 
927
- // 6) log:query can produce Turtle; that should stay in plain source output without Markdown tabs.
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');
955
+ endTest();
956
+
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(
@@ -973,9 +1003,25 @@ ${JSON.stringify(last, null, 2)}`);
973
1003
  new RegExp('href="' + started.baseUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '/examples/input/smoke-arithmetic\\.trig"'),
974
1004
  'Expected relative Markdown TriG links to resolve against the static output page, not /playground',
975
1005
  );
1006
+ assert.equal(smoke.outputTabsHidden, false, 'Expected smoke-arithmetic Markdown output tabs to be visible');
1007
+ assert.equal(smoke.renderedHidden, false, 'Expected smoke-arithmetic Markdown output to render by default');
1008
+ assert.equal(smoke.sourceHidden, true, 'Expected smoke-arithmetic Markdown source to be hidden by default');
1009
+
1010
+ await clickOutputSourceTab();
1011
+ const smokeSourceView = await getPlaygroundState();
1012
+ assert.equal(smokeSourceView.sourceTabSelected, true, 'Expected smoke-arithmetic Markdown source tab to be selectable');
1013
+ assert.equal(smokeSourceView.renderedHidden, true, 'Expected smoke-arithmetic rendered panel to hide in source view');
1014
+ assert.equal(smokeSourceView.sourceHidden, false, 'Expected smoke-arithmetic source editor to show in source view');
1015
+ assert.match(smokeSourceView.output, /^# smoke-arithmetic/m, 'Expected smoke-arithmetic source tab to show Markdown source');
1016
+
1017
+ await clickOutputRenderedTab();
1018
+ const smokeRenderedAgain = await getPlaygroundState();
1019
+ assert.equal(smokeRenderedAgain.renderedTabSelected, true, 'Expected smoke-arithmetic Rendered tab to be selectable again');
1020
+ assert.equal(smokeRenderedAgain.renderedHidden, false, 'Expected smoke-arithmetic rendered panel to show again');
1021
+ assert.equal(smokeRenderedAgain.sourceHidden, true, 'Expected smoke-arithmetic source editor to hide again');
976
1022
  endTest();
977
1023
 
978
- // 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.
979
1025
  beginTest('playground auto-loads a companion example builtin for URL-loaded Sudoku');
980
1026
  await loadUrlIntoEditor('https://raw.githubusercontent.com/eyereasoner/eyeling/refs/heads/main/examples/sudoku.n3');
981
1027
  await waitForState(
@@ -999,12 +1045,12 @@ ${JSON.stringify(last, null, 2)}`);
999
1045
  assert.match(sudoku.output, /unique valid Sudoku solution/i, 'Expected Sudoku builtin-backed result');
1000
1046
  endTest();
1001
1047
 
1002
- // 9) Ensure no uncaught runtime exceptions.
1048
+ // 10) Ensure no uncaught runtime exceptions.
1003
1049
  beginTest('playground has no uncaught runtime exceptions');
1004
1050
  assert.equal(exceptions.length, 0, `Uncaught exceptions in playground.html: ${JSON.stringify(exceptions[0] || {})}`);
1005
1051
  endTest();
1006
1052
 
1007
- // 10) Console errors are noisy and often indicate a broken UI.
1053
+ // 11) Console errors are noisy and often indicate a broken UI.
1008
1054
  // (We suppress known noise like /favicon.ico on the server.)
1009
1055
  beginTest('playground has no console errors');
1010
1056
  assert.equal(consoleErrors.length, 0, `Console errors in playground.html: ${JSON.stringify(consoleErrors[0] || {})}`);