eyeling 1.24.17 → 1.24.19
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 +2 -0
- package/package.json +1 -1
- package/test/playground.test.js +66 -5
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 a **Create TinyURL** option. The threshold is intentionally conservative, and the shortener handoff is explicit rather than automatic: after the user chooses that option, the browser uses a TinyURL API token stored locally in that browser to create the short link and copy it to the clipboard. If no token is provided, or if the API request fails, the playground falls back to opening TinyURL and copying the long compact share link so it can be pasted manually. This avoids silently sending encoded editor content to a third-party service while still making the account-backed TinyURL workflow one click after setup.
|
|
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
package/test/playground.test.js
CHANGED
|
@@ -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,42 @@ 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
|
+
|
|
793
|
+
async function createTinyUrlWithStubInPage(longUrl, token, response) {
|
|
794
|
+
const payload = JSON.stringify({ longUrl: String(longUrl), token: String(token), response });
|
|
795
|
+
return await evalInPage(`(async () => {
|
|
796
|
+
const args = ${payload};
|
|
797
|
+
const originalFetch = window.fetch;
|
|
798
|
+
let seen = null;
|
|
799
|
+
window.fetch = async (url, options) => {
|
|
800
|
+
seen = { url: String(url || ''), options: options || null };
|
|
801
|
+
return {
|
|
802
|
+
ok: true,
|
|
803
|
+
status: 200,
|
|
804
|
+
json: async () => args.response,
|
|
805
|
+
};
|
|
806
|
+
};
|
|
807
|
+
try {
|
|
808
|
+
const tinyUrl = await window.__eyelingPlaygroundCreateTinyUrl(args.longUrl, args.token);
|
|
809
|
+
return { tinyUrl, seen };
|
|
810
|
+
} finally {
|
|
811
|
+
window.fetch = originalFetch;
|
|
812
|
+
}
|
|
813
|
+
})()`);
|
|
814
|
+
}
|
|
815
|
+
|
|
779
816
|
async function loadUrlIntoEditor(url) {
|
|
780
817
|
const payload = JSON.stringify(String(url));
|
|
781
818
|
await evalInPage(`(() => {
|
|
@@ -922,9 +959,33 @@ ${JSON.stringify(last, null, 2)}`);
|
|
|
922
959
|
assert.match(compactShareUrl, /[?&]state=/, 'Expected an on-demand compact state parameter');
|
|
923
960
|
assert.doesNotMatch(compactShareUrl, /[?&](?:edit|program)=/, 'Expected share link to avoid raw edit/program params');
|
|
924
961
|
assert.ok(compactShareUrl.length < playgroundUrl.length + encodeURIComponent(outputStringProgram).length, 'Expected compact share URL to be shorter than raw editor URL');
|
|
962
|
+
assert.equal(renderedAgain.shortenerHidden, true, 'Expected ordinary compact share links to keep the shortener option hidden');
|
|
963
|
+
endTest();
|
|
964
|
+
|
|
965
|
+
// 6) Very large edited programs should offer a URL shortener handoff instead of only a huge link.
|
|
966
|
+
beginTest('playground offers a URL shortener option for oversized share links');
|
|
967
|
+
const longShareProgram = Array.from({ length: 1400 }, (_, i) => {
|
|
968
|
+
const n = String(i).padStart(4, '0');
|
|
969
|
+
const token = ((i * 2654435761) >>> 0).toString(36).padStart(7, '0');
|
|
970
|
+
return `:s${n}_${token} :p${token}_${n} "literal-${n}-${token}-${i * 9973}" .`;
|
|
971
|
+
}).join('\n');
|
|
972
|
+
await setProgram(longShareProgram);
|
|
973
|
+
const longShare = await makeShareUrlDiagnosticsInPage();
|
|
974
|
+
assert.ok(longShare.length > longShare.threshold, `Expected test share URL to exceed threshold (${longShare.length} <= ${longShare.threshold})`);
|
|
975
|
+
assert.equal(longShare.needsShortener, true, 'Expected oversized share URL to request a shortener option');
|
|
976
|
+
assert.match(longShare.shortenerUrl, /^https:\/\/tinyurl\.com\/app\?url=/, 'Expected TinyURL app fallback handoff');
|
|
977
|
+
assert.ok(longShare.shortenerUrl.includes(encodeURIComponent(longShare.url).slice(0, 40)), 'Expected shortener URL to carry the generated share URL');
|
|
978
|
+
const tinyUrlCreated = await createTinyUrlWithStubInPage(longShare.url, 'test-token-123', {
|
|
979
|
+
data: { tiny_url: 'https://tinyurl.com/eyeling-test' },
|
|
980
|
+
});
|
|
981
|
+
assert.equal(tinyUrlCreated.tinyUrl, 'https://tinyurl.com/eyeling-test', 'Expected TinyURL API response to produce a short URL');
|
|
982
|
+
assert.equal(tinyUrlCreated.seen.url, 'https://api.tinyurl.com/create', 'Expected TinyURL API create endpoint');
|
|
983
|
+
assert.equal(tinyUrlCreated.seen.options.method, 'POST', 'Expected TinyURL API POST request');
|
|
984
|
+
assert.equal(tinyUrlCreated.seen.options.headers.Authorization, 'Bearer test-token-123', 'Expected bearer token authorization');
|
|
985
|
+
assert.match(String(tinyUrlCreated.seen.options.body || ''), /"url":/, 'Expected TinyURL API body to include the long URL');
|
|
925
986
|
endTest();
|
|
926
987
|
|
|
927
|
-
//
|
|
988
|
+
// 7) log:query can produce Turtle; that should stay in plain source output without Markdown tabs.
|
|
928
989
|
beginTest('playground hides markdown tabs for Turtle log:query output');
|
|
929
990
|
await setProgram(logQueryTurtleProgram);
|
|
930
991
|
await clickRun();
|
|
@@ -944,7 +1005,7 @@ ${JSON.stringify(last, null, 2)}`);
|
|
|
944
1005
|
assert.equal(logQueryTurtle.sourceHidden, false, 'Expected Turtle log:query output to show source directly');
|
|
945
1006
|
endTest();
|
|
946
1007
|
|
|
947
|
-
//
|
|
1008
|
+
// 8) URL-loaded examples should auto-load matching examples/input/<stem>.trig and run in RDF/TriG mode.
|
|
948
1009
|
beginTest('playground auto-loads companion TriG sidecars and uses RDF/TriG mode');
|
|
949
1010
|
await loadUrlIntoEditor('https://raw.githubusercontent.com/eyereasoner/eyeling/refs/heads/main/examples/smoke-arithmetic.n3');
|
|
950
1011
|
const smokeLoaded = await waitForState(
|
|
@@ -991,7 +1052,7 @@ ${JSON.stringify(last, null, 2)}`);
|
|
|
991
1052
|
assert.equal(smokeRenderedAgain.sourceHidden, true, 'Expected smoke-arithmetic source editor to hide again');
|
|
992
1053
|
endTest();
|
|
993
1054
|
|
|
994
|
-
//
|
|
1055
|
+
// 9) URL-loaded repository examples should auto-load matching examples/builtin/<stem>.js.
|
|
995
1056
|
beginTest('playground auto-loads a companion example builtin for URL-loaded Sudoku');
|
|
996
1057
|
await loadUrlIntoEditor('https://raw.githubusercontent.com/eyereasoner/eyeling/refs/heads/main/examples/sudoku.n3');
|
|
997
1058
|
await waitForState(
|
|
@@ -1015,12 +1076,12 @@ ${JSON.stringify(last, null, 2)}`);
|
|
|
1015
1076
|
assert.match(sudoku.output, /unique valid Sudoku solution/i, 'Expected Sudoku builtin-backed result');
|
|
1016
1077
|
endTest();
|
|
1017
1078
|
|
|
1018
|
-
//
|
|
1079
|
+
// 10) Ensure no uncaught runtime exceptions.
|
|
1019
1080
|
beginTest('playground has no uncaught runtime exceptions');
|
|
1020
1081
|
assert.equal(exceptions.length, 0, `Uncaught exceptions in playground.html: ${JSON.stringify(exceptions[0] || {})}`);
|
|
1021
1082
|
endTest();
|
|
1022
1083
|
|
|
1023
|
-
//
|
|
1084
|
+
// 11) Console errors are noisy and often indicate a broken UI.
|
|
1024
1085
|
// (We suppress known noise like /favicon.ico on the server.)
|
|
1025
1086
|
beginTest('playground has no console errors');
|
|
1026
1087
|
assert.equal(consoleErrors.length, 0, `Console errors in playground.html: ${JSON.stringify(consoleErrors[0] || {})}`);
|