composite-monaco-diff 1.0.2
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/LICENSE +338 -0
- package/dist/cjs/CenterAndHeightResizer.cjs +325 -0
- package/dist/cjs/CenterResizer.cjs +195 -0
- package/dist/cjs/Module.cjs +12 -0
- package/dist/cjs/MonacoDiffManager.cjs +306 -0
- package/dist/cjs/composite-monaco-diff.cjs +123 -0
- package/dist/cjs/manager/index.cjs +177 -0
- package/dist/cjs/react.cjs +64 -0
- package/dist/cjs/trimLeft.cjs +28 -0
- package/dist/cjs/urlchange/ChildSection.cjs +174 -0
- package/dist/cjs/urlchange/index.cjs +229 -0
- package/dist/cjs/urlchange/toolsURLSearchParams.cjs +111 -0
- package/dist/cjs/urlchange/urlchange.cjs +197 -0
- package/dist/cjs/web-component/from-js/index.cjs +160 -0
- package/dist/cjs/web-component/from-scripts/index.cjs +114 -0
- package/dist/esm/CenterAndHeightResizer.js +325 -0
- package/dist/esm/CenterResizer.js +195 -0
- package/dist/esm/Module.js +12 -0
- package/dist/esm/MonacoDiffManager.js +306 -0
- package/dist/esm/composite-monaco-diff.js +123 -0
- package/dist/esm/manager/index.js +177 -0
- package/dist/esm/react.js +64 -0
- package/dist/esm/trimLeft.js +28 -0
- package/dist/esm/urlchange/ChildSection.js +174 -0
- package/dist/esm/urlchange/index.js +229 -0
- package/dist/esm/urlchange/toolsURLSearchParams.js +111 -0
- package/dist/esm/urlchange/urlchange.js +197 -0
- package/dist/esm/web-component/from-js/index.js +160 -0
- package/dist/esm/web-component/from-scripts/index.js +114 -0
- package/dist/types/CenterAndHeightResizer.d.ts +27 -0
- package/dist/types/CenterResizer.d.ts +16 -0
- package/dist/types/Module.d.ts +11 -0
- package/dist/types/MonacoDiffManager.d.ts +62 -0
- package/dist/types/composite-monaco-diff.d.ts +48 -0
- package/dist/types/manager/index.d.ts +1 -0
- package/dist/types/react.d.ts +19 -0
- package/dist/types/trimLeft.d.ts +1 -0
- package/dist/types/urlchange/ChildSection.d.ts +41 -0
- package/dist/types/urlchange/index.d.ts +1 -0
- package/dist/types/urlchange/toolsURLSearchParams.d.ts +40 -0
- package/dist/types/urlchange/urlchange.d.ts +107 -0
- package/dist/types/web-component/from-js/index.d.ts +1 -0
- package/dist/types/web-component/from-scripts/index.d.ts +1 -0
- package/package.json +44 -0
- package/web-component/Module.js +413 -0
- package/web-component/MonacoDiffManager.js +306 -0
- package/web-component/composite-monaco-diff.js +123 -0
- package/web-component/react.js +64 -0
- package/web-component/trimLeft.js +28 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges multiple URLSearchParams instances. The first parameter acts as the base.
|
|
3
|
+
* Subsequent parameters overwrite existing keys. If a string array is provided,
|
|
4
|
+
* it acts as an allowlist, meaning only those keys will be merged from subsequent instances.
|
|
5
|
+
* The final result is sorted alphabetically.
|
|
6
|
+
*/
|
|
7
|
+
export function mergeURLSearchParams(...args) {
|
|
8
|
+
const result = new URLSearchParams();
|
|
9
|
+
if (args.length === 0)
|
|
10
|
+
return result;
|
|
11
|
+
// Find the filter array if it exists
|
|
12
|
+
const filterIndex = args.findIndex((arg) => Array.isArray(arg));
|
|
13
|
+
const filterKeys = filterIndex !== -1 ? args[filterIndex] : null;
|
|
14
|
+
const filterSet = filterKeys ? new Set(filterKeys) : null;
|
|
15
|
+
for (let i = 0; i < args.length; i++) {
|
|
16
|
+
const params = args[i];
|
|
17
|
+
if (params instanceof URLSearchParams) {
|
|
18
|
+
// The first URLSearchParams contributes all its keys.
|
|
19
|
+
// Subsequent ones only contribute keys in the filterSet (if defined).
|
|
20
|
+
const isFirstParams = i === args.findIndex((arg) => arg instanceof URLSearchParams);
|
|
21
|
+
const uniqueKeys = new Set();
|
|
22
|
+
params.forEach((_, key) => uniqueKeys.add(key));
|
|
23
|
+
const keys = Array.from(uniqueKeys);
|
|
24
|
+
const keysToProcess = !isFirstParams && filterSet ? keys.filter((k) => filterSet.has(k)) : keys;
|
|
25
|
+
for (const key of keysToProcess) {
|
|
26
|
+
result.delete(key);
|
|
27
|
+
params.getAll(key).forEach((value) => {
|
|
28
|
+
result.append(key, value);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
result.sort();
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Creates a deep, distinct copy of a URLSearchParams instance, preserving its data
|
|
38
|
+
* but decoupling its object reference so mutations don't affect the original.
|
|
39
|
+
*/
|
|
40
|
+
export function cloneSearchParams(params) {
|
|
41
|
+
return new URLSearchParams(params);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Returns a new URLSearchParams instance with all keys sorted alphabetically.
|
|
45
|
+
* Useful for ensuring consistent parameter ordering before serialization.
|
|
46
|
+
*/
|
|
47
|
+
export function normalizeSearchParams(params) {
|
|
48
|
+
const result = new URLSearchParams(params);
|
|
49
|
+
result.sort();
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Compares two URLSearchParams instances for equality by normalizing and sorting them.
|
|
54
|
+
* Returns true if they contain the exact same keys and values regardless of original order.
|
|
55
|
+
*/
|
|
56
|
+
export function compareNormalizedSearchParams(a, b) {
|
|
57
|
+
const na = normalizeSearchParams(a);
|
|
58
|
+
const nb = normalizeSearchParams(b);
|
|
59
|
+
return na.toString() === nb.toString();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns a new URLSearchParams instance sorted primarily by keys,
|
|
63
|
+
* and secondarily by values if the keys are identical.
|
|
64
|
+
*/
|
|
65
|
+
export function sortSearchParamsByKeyThenValue(params) {
|
|
66
|
+
const entries = [];
|
|
67
|
+
params.forEach((value, key) => {
|
|
68
|
+
entries.push([key, value]);
|
|
69
|
+
});
|
|
70
|
+
return new URLSearchParams(entries.sort(([k1, v1], [k2, v2]) => {
|
|
71
|
+
if (k1 === k2)
|
|
72
|
+
return v1.localeCompare(v2);
|
|
73
|
+
return k1.localeCompare(k2);
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Merges consecutive source URLSearchParams into a base URLSearchParams, but strictly
|
|
78
|
+
* limits updates and deletions to a specific list of governed keys.
|
|
79
|
+
* Sources are applied in sequence. For each key in governedKeys and each source:
|
|
80
|
+
* - If the key is present in the source, it overwrites the value in result.
|
|
81
|
+
* - If the key is absent from the source, it is deleted from the result.
|
|
82
|
+
*/
|
|
83
|
+
export function syncURLSearchParams(base, governedKeys, ...sources) {
|
|
84
|
+
const result = new URLSearchParams(base);
|
|
85
|
+
const keys = Array.isArray(governedKeys) ? governedKeys : Array.from(governedKeys);
|
|
86
|
+
for (const source of sources) {
|
|
87
|
+
for (const key of keys) {
|
|
88
|
+
if (source.has(key)) {
|
|
89
|
+
result.delete(key);
|
|
90
|
+
source.getAll(key).forEach((value) => {
|
|
91
|
+
result.append(key, value);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
result.delete(key);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
result.sort();
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Updates a URL string with new search parameters, preserving origin, pathname, and hash.
|
|
104
|
+
* Handles both absolute and relative locations.
|
|
105
|
+
*/
|
|
106
|
+
export function buildUrlWithSearchParams(location, nextParams) {
|
|
107
|
+
const search = typeof nextParams === "string" ? nextParams : nextParams.toString();
|
|
108
|
+
const url = new URL(location, "http://dummy");
|
|
109
|
+
url.search = search ? `?${search}` : "";
|
|
110
|
+
return url.origin === "http://dummy" ? `${url.pathname}${url.search}${url.hash}` : url.toString();
|
|
111
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { cloneSearchParams, compareNormalizedSearchParams, mergeURLSearchParams, syncURLSearchParams, } from "./toolsURLSearchParams.js";
|
|
2
|
+
/** Set of active listeners registered for URL/history updates. */
|
|
3
|
+
const urlChangeListeners = new Set();
|
|
4
|
+
/** Flag indicating whether the window.history methods have been monkey-patched. */
|
|
5
|
+
let historyPatched = false;
|
|
6
|
+
let originalPushState = null;
|
|
7
|
+
let originalReplaceState = null;
|
|
8
|
+
/**
|
|
9
|
+
* Notifies all registered listeners that a URL change has occurred.
|
|
10
|
+
*/
|
|
11
|
+
function notifyUrlChange() {
|
|
12
|
+
for (const listener of urlChangeListeners) {
|
|
13
|
+
listener();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Monkey-patches history.pushState and history.replaceState to notify our listeners
|
|
18
|
+
* of programmatic URL changes. Also listens to the window popstate event.
|
|
19
|
+
*/
|
|
20
|
+
function ensureHistoryPatched() {
|
|
21
|
+
if (historyPatched || typeof window === "undefined")
|
|
22
|
+
return;
|
|
23
|
+
historyPatched = true;
|
|
24
|
+
originalPushState = history.pushState.bind(history);
|
|
25
|
+
originalReplaceState = history.replaceState.bind(history);
|
|
26
|
+
history.pushState = function (...args) {
|
|
27
|
+
originalPushState(...args);
|
|
28
|
+
notifyUrlChange();
|
|
29
|
+
};
|
|
30
|
+
history.replaceState = function (...args) {
|
|
31
|
+
originalReplaceState(...args);
|
|
32
|
+
notifyUrlChange();
|
|
33
|
+
};
|
|
34
|
+
window.addEventListener("popstate", notifyUrlChange);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Subscribes a listener callback to URL change events (pushState, replaceState, popstate).
|
|
38
|
+
* Returns an unsubscribe function.
|
|
39
|
+
*/
|
|
40
|
+
function subscribeUrlChange(listener) {
|
|
41
|
+
ensureHistoryPatched();
|
|
42
|
+
urlChangeListeners.add(listener);
|
|
43
|
+
return () => {
|
|
44
|
+
urlChangeListeners.delete(listener);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// ─── Core factory ────────────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Internal factory that creates trackers for a specific configuration of URL parameters.
|
|
50
|
+
* Encapsulates the logic of reading, writing, and separating parameter sets.
|
|
51
|
+
*/
|
|
52
|
+
function createURLParamTracker(config, keyFn) {
|
|
53
|
+
/** Applies optional keyFn namespace/index mapping to raw config parameter keys. */
|
|
54
|
+
const applyKey = (baseKey, ctx) => (keyFn ? keyFn(baseKey, ctx) : baseKey);
|
|
55
|
+
/**
|
|
56
|
+
* Filters and extracts only the keys governed by this tracker configuration and optional context
|
|
57
|
+
* from the provided search parameters or string.
|
|
58
|
+
*/
|
|
59
|
+
function separateIndexedSearchParams(search, ctx) {
|
|
60
|
+
const keys = Object.values(config).map((def) => applyKey(def.getParam, ctx));
|
|
61
|
+
const normalized = typeof search === "string" ? new URLSearchParams(search) : search;
|
|
62
|
+
return mergeURLSearchParams(new URLSearchParams(), keys, normalized);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reads, decodes, and parses parameter values from search parameters, falling back to defaults.
|
|
66
|
+
*/
|
|
67
|
+
function readState(search, ctx) {
|
|
68
|
+
const updatedURLSearchParams = separateIndexedSearchParams(search, ctx);
|
|
69
|
+
const result = {};
|
|
70
|
+
for (const [key, def] of Object.entries(config)) {
|
|
71
|
+
const d = def;
|
|
72
|
+
const raw = updatedURLSearchParams.get(applyKey(d.getParam, ctx));
|
|
73
|
+
result[key] = raw !== null ? d.decode(raw) : d.default;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
params: result,
|
|
77
|
+
updatedURLSearchParams,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Generates a normalized signature of only the tracked keys for comparison. */
|
|
81
|
+
function trackedSignature(search, ctx) {
|
|
82
|
+
return separateIndexedSearchParams(search, ctx).toString();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Starts tracking the URL, invoking the onChange callback when tracked parameters change.
|
|
86
|
+
* Returns a handle containing functions to read/update the state.
|
|
87
|
+
*/
|
|
88
|
+
function trackUrl(onChange, options) {
|
|
89
|
+
const ctx = options?.ctx;
|
|
90
|
+
const replace = options?.replace !== false;
|
|
91
|
+
const fireOnMount = options?.fireOnMount !== false;
|
|
92
|
+
const governedKeys = Object.values(config).map((def) => applyKey(def.getParam, ctx));
|
|
93
|
+
let lastSignature = "";
|
|
94
|
+
/** Decodes the current URL parameters and invokes the onChange callback. */
|
|
95
|
+
const emitCurrentState = (search = window.location.search) => {
|
|
96
|
+
lastSignature = trackedSignature(search, ctx);
|
|
97
|
+
const { params, updatedURLSearchParams } = readState(search, ctx);
|
|
98
|
+
onChange(params, updatedURLSearchParams, governedKeys);
|
|
99
|
+
};
|
|
100
|
+
/** Checks if the signature of the tracked parameters changed, and triggers update if so. */
|
|
101
|
+
const emitIfTrackedChanged = () => {
|
|
102
|
+
const signature = trackedSignature(window.location.search, ctx);
|
|
103
|
+
if (signature === lastSignature)
|
|
104
|
+
return;
|
|
105
|
+
emitCurrentState();
|
|
106
|
+
};
|
|
107
|
+
/** Syncs internal state signature and calls onChange callback after writing updates. */
|
|
108
|
+
const syncAfterLocalWrite = (next) => {
|
|
109
|
+
emitCurrentState(next);
|
|
110
|
+
};
|
|
111
|
+
/** Commits the new URLSearchParams using standard history methods. */
|
|
112
|
+
const writeSearch = (next) => {
|
|
113
|
+
const current = cloneSearchParams(new URLSearchParams(window.location.search));
|
|
114
|
+
if (compareNormalizedSearchParams(next, current))
|
|
115
|
+
return;
|
|
116
|
+
syncAfterLocalWrite(next);
|
|
117
|
+
};
|
|
118
|
+
/** Helper to set/update a single parameter value. */
|
|
119
|
+
const setParam = (key, value) => {
|
|
120
|
+
const def = config[key];
|
|
121
|
+
const finalKey = applyKey(def.getParam, ctx);
|
|
122
|
+
const current = cloneSearchParams(new URLSearchParams(window.location.search));
|
|
123
|
+
const patch = new URLSearchParams();
|
|
124
|
+
if (JSON.stringify(value) !== JSON.stringify(def.default)) {
|
|
125
|
+
patch.set(finalKey, def.encode(value));
|
|
126
|
+
}
|
|
127
|
+
writeSearch(syncURLSearchParams(current, [finalKey], patch));
|
|
128
|
+
};
|
|
129
|
+
/** Helper to set/update multiple parameter values at once. */
|
|
130
|
+
const setParams = (updates) => {
|
|
131
|
+
const current = cloneSearchParams(new URLSearchParams(window.location.search));
|
|
132
|
+
const governed = [];
|
|
133
|
+
const patch = new URLSearchParams();
|
|
134
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
135
|
+
const def = config[key];
|
|
136
|
+
if (def && value !== undefined) {
|
|
137
|
+
const finalKey = applyKey(def.getParam, ctx);
|
|
138
|
+
governed.push(finalKey);
|
|
139
|
+
if (JSON.stringify(value) !== JSON.stringify(def.default)) {
|
|
140
|
+
patch.set(finalKey, def.encode(value));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
writeSearch(syncURLSearchParams(current, governed, patch));
|
|
145
|
+
};
|
|
146
|
+
if (fireOnMount) {
|
|
147
|
+
emitCurrentState();
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
lastSignature = trackedSignature(window.location.search, ctx);
|
|
151
|
+
}
|
|
152
|
+
const unsubscribe = subscribeUrlChange(emitIfTrackedChanged);
|
|
153
|
+
return {
|
|
154
|
+
setParam,
|
|
155
|
+
setParams,
|
|
156
|
+
getParams: () => readState(window.location.search, ctx).params,
|
|
157
|
+
getUpdatedURLSearchParams: () => separateIndexedSearchParams(window.location.search, ctx),
|
|
158
|
+
refresh: () => emitCurrentState(),
|
|
159
|
+
disconnect: unsubscribe,
|
|
160
|
+
governedKeys,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
separateIndexedSearchParams,
|
|
165
|
+
trackUrl,
|
|
166
|
+
readState,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Vanilla counterpart to React `modURLSearchParams`. Define params once, then call
|
|
171
|
+
* `trackUrl(onChange)` or use the standalone `trackUrl(config, onChange)` helper.
|
|
172
|
+
*/
|
|
173
|
+
export default function modURLSearchParams(config, keyFn) {
|
|
174
|
+
return createURLParamTracker(config, keyFn);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Subscribe to URL changes for a fixed param schema. The callback runs only when
|
|
178
|
+
* tracked keys (per config) change — other query/hash navigation is ignored.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* const { setParam, setParams } = trackUrl(
|
|
182
|
+
* {
|
|
183
|
+
* emptyList: { default: false, getParam: "emp", encode: (v) => (v ? "1" : "0"), decode: (v) => v === "1" },
|
|
184
|
+
* },
|
|
185
|
+
* (params, updatedURLSearchParams) => { ... },
|
|
186
|
+
* );
|
|
187
|
+
*/
|
|
188
|
+
export function trackUrl(config, onChange, options) {
|
|
189
|
+
const { keyFn, ...trackOptions } = options ?? {};
|
|
190
|
+
const tracker = createURLParamTracker(config, keyFn);
|
|
191
|
+
return tracker.trackUrl(onChange, trackOptions);
|
|
192
|
+
}
|
|
193
|
+
/** Subscribe to query-string updates (history push/replace/popstate). */
|
|
194
|
+
export function onUrlChange(listener) {
|
|
195
|
+
return subscribeUrlChange(listener);
|
|
196
|
+
}
|
|
197
|
+
export { mergeURLSearchParams, createURLParamTracker, compareNormalizedSearchParams };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import modURLSearchParams from "../../urlchange/urlchange.js";
|
|
2
|
+
import { syncURLSearchParams, buildUrlWithSearchParams } from "../../urlchange/toolsURLSearchParams.js";
|
|
3
|
+
import { MonacoDiffManager } from "../../MonacoDiffManager.js";
|
|
4
|
+
import { isMonacoTheme, MonacoDiffElement } from "../../composite-monaco-diff.js";
|
|
5
|
+
import { CenterAndHeightResizer } from "../../CenterAndHeightResizer.js";
|
|
6
|
+
await customElements.whenDefined(MonacoDiffElement.tagName);
|
|
7
|
+
await customElements.whenDefined(CenterAndHeightResizer.tagName);
|
|
8
|
+
const diffEl = document.querySelector(MonacoDiffElement.tagName);
|
|
9
|
+
if (!(diffEl instanceof MonacoDiffElement)) {
|
|
10
|
+
throw new Error("Missing <composite-monaco-diff> element");
|
|
11
|
+
}
|
|
12
|
+
await diffEl.whenReady();
|
|
13
|
+
let model;
|
|
14
|
+
let mgr;
|
|
15
|
+
const original = `
|
|
16
|
+
const loadMonaco = (vsPath = VS_PATH) =>
|
|
17
|
+
new Promise((resolve, reject) => {
|
|
18
|
+
const win = window;
|
|
19
|
+
|
|
20
|
+
const finish = () => {
|
|
21
|
+
win.require.config({ paths: { vs: vsPath } });
|
|
22
|
+
win.require(["vs/editor/editor.main"], () => resolve(win.monaco));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (win.require && win.monaco) return resolve(win.monaco);
|
|
26
|
+
if (win.require) return finish();
|
|
27
|
+
|
|
28
|
+
const script = document.createElement("script");
|
|
29
|
+
script.src = \`\${vsPath}/loader.js\`;
|
|
30
|
+
script.async = true;
|
|
31
|
+
script.onload = () => finish();
|
|
32
|
+
script.onerror = () => reject(new Error(\`Failed to load Monaco loader from \${vsPath}\`));
|
|
33
|
+
document.head.appendChild(script);
|
|
34
|
+
});
|
|
35
|
+
`;
|
|
36
|
+
const modified = `
|
|
37
|
+
const loadMonaco = (vsPath = VS_PATH) =>
|
|
38
|
+
new Promise((resolve, reject) => {
|
|
39
|
+
const win = window;
|
|
40
|
+
|
|
41
|
+
const finish = () => {
|
|
42
|
+
win.require.config({ paths: { vs: vsPath } });
|
|
43
|
+
win.require(["vs/editor/editor.main"], () => resolve(win.monaco));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (win.require && win.moneco) return resolve(win.monaco);
|
|
47
|
+
|
|
48
|
+
const script = document.createElement("script");
|
|
49
|
+
script.src = \`\${vsPath}/loader.js\`;
|
|
50
|
+
script.async = true;
|
|
51
|
+
script.onload = () => finish();
|
|
52
|
+
script.added = 'stuff'
|
|
53
|
+
script.onerror = () => reject(new Error(\`Failed to load Monaco loader from \${vsPath}\`));
|
|
54
|
+
document.head.appendChild(script);
|
|
55
|
+
});
|
|
56
|
+
`;
|
|
57
|
+
function wireResizerUrlSync(resizer, index) {
|
|
58
|
+
const config = {
|
|
59
|
+
left: {
|
|
60
|
+
default: resizer.getAttribute("left") ?? "100px",
|
|
61
|
+
getParam: "l",
|
|
62
|
+
encode: (value) => value,
|
|
63
|
+
decode: (value) => value,
|
|
64
|
+
},
|
|
65
|
+
center: {
|
|
66
|
+
default: resizer.getAttribute("center") ?? "1200px",
|
|
67
|
+
getParam: "c",
|
|
68
|
+
encode: (value) => value,
|
|
69
|
+
decode: (value) => value,
|
|
70
|
+
},
|
|
71
|
+
height: {
|
|
72
|
+
default: resizer.getAttribute("height") ?? "100px",
|
|
73
|
+
getParam: "h",
|
|
74
|
+
encode: (value) => value,
|
|
75
|
+
decode: (value) => value,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const { trackUrl } = modURLSearchParams(config, (key, i) => `${key}-${i}`);
|
|
79
|
+
const { setParams } = trackUrl((params, updatedURLSearchParams, governedKeys) => {
|
|
80
|
+
resizer.setAttribute("left", params.left);
|
|
81
|
+
resizer.setAttribute("center", params.center);
|
|
82
|
+
resizer.setAttribute("height", params.height);
|
|
83
|
+
const current = new URLSearchParams(window.location.search);
|
|
84
|
+
const next = syncURLSearchParams(current, governedKeys, updatedURLSearchParams);
|
|
85
|
+
if (next.toString() !== current.toString()) {
|
|
86
|
+
const url = buildUrlWithSearchParams(window.location.href, next);
|
|
87
|
+
history.replaceState(history.state, "", url);
|
|
88
|
+
}
|
|
89
|
+
}, { ctx: index, fireOnMount: true });
|
|
90
|
+
const syncToUrl = () => {
|
|
91
|
+
setParams({
|
|
92
|
+
left: resizer.getAttribute("left") ?? config.left.default,
|
|
93
|
+
center: resizer.getAttribute("center") ?? config.center.default,
|
|
94
|
+
height: resizer.getAttribute("height") ?? config.height.default,
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
resizer.addEventListener("onLeft", syncToUrl);
|
|
98
|
+
resizer.addEventListener("onCenter", syncToUrl);
|
|
99
|
+
resizer.addEventListener("onHeight", syncToUrl);
|
|
100
|
+
}
|
|
101
|
+
function applyThemeAttribute(mgr, theme) {
|
|
102
|
+
mgr.getMonaco()?.editor.setTheme(theme || "vs");
|
|
103
|
+
}
|
|
104
|
+
function applyLanguageAttribute(mgr, language) {
|
|
105
|
+
mgr.setLanguage(language || undefined);
|
|
106
|
+
}
|
|
107
|
+
document.querySelectorAll(CenterAndHeightResizer.tagName).forEach((el, index) => {
|
|
108
|
+
wireResizerUrlSync(el, index);
|
|
109
|
+
});
|
|
110
|
+
const themeSelect = document.getElementById("theme-select");
|
|
111
|
+
if (!(themeSelect instanceof HTMLSelectElement)) {
|
|
112
|
+
throw new Error("Missing #theme-select element");
|
|
113
|
+
}
|
|
114
|
+
const languageSelect = document.getElementById("language-select");
|
|
115
|
+
if (!(languageSelect instanceof HTMLSelectElement)) {
|
|
116
|
+
throw new Error("Missing #language-select element");
|
|
117
|
+
}
|
|
118
|
+
const { trackUrl: trackUrlNoIndex } = modURLSearchParams({
|
|
119
|
+
theme: {
|
|
120
|
+
default: "",
|
|
121
|
+
getParam: "theme",
|
|
122
|
+
encode: (value) => value,
|
|
123
|
+
decode: (value) => (isMonacoTheme(value) ? value : ""),
|
|
124
|
+
},
|
|
125
|
+
language: {
|
|
126
|
+
default: "javascript",
|
|
127
|
+
getParam: "lang",
|
|
128
|
+
encode: (value) => value,
|
|
129
|
+
decode: (value) => value,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
const { setParam } = trackUrlNoIndex((params, updatedURLSearchParams, governedKeys) => {
|
|
133
|
+
themeSelect.value = params.theme;
|
|
134
|
+
applyThemeAttribute(mgr, params.theme);
|
|
135
|
+
languageSelect.value = params.language;
|
|
136
|
+
applyLanguageAttribute(mgr, params.language);
|
|
137
|
+
const current = new URLSearchParams(window.location.search);
|
|
138
|
+
const next = syncURLSearchParams(current, governedKeys, updatedURLSearchParams);
|
|
139
|
+
if (next.toString() !== current.toString()) {
|
|
140
|
+
const url = buildUrlWithSearchParams(window.location.href, next);
|
|
141
|
+
history.replaceState(history.state, "", url);
|
|
142
|
+
}
|
|
143
|
+
}, { fireOnMount: true });
|
|
144
|
+
themeSelect.addEventListener("change", () => {
|
|
145
|
+
setParam("theme", themeSelect.value);
|
|
146
|
+
});
|
|
147
|
+
languageSelect.addEventListener("change", () => {
|
|
148
|
+
setParam("language", languageSelect.value);
|
|
149
|
+
});
|
|
150
|
+
mgr = diffEl.getManager();
|
|
151
|
+
const editor = mgr.getEditor();
|
|
152
|
+
if (!editor) {
|
|
153
|
+
throw new Error("Diff editor not available");
|
|
154
|
+
}
|
|
155
|
+
model = editor.getModel();
|
|
156
|
+
if (!model) {
|
|
157
|
+
throw new Error("Diff editor has no model");
|
|
158
|
+
}
|
|
159
|
+
model.original.setValue(original);
|
|
160
|
+
model.modified.setValue(modified);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import modURLSearchParams from "../../urlchange/urlchange.js";
|
|
2
|
+
import { syncURLSearchParams, buildUrlWithSearchParams } from "../../urlchange/toolsURLSearchParams.js";
|
|
3
|
+
import { isMonacoTheme, MonacoDiffElement } from "../../composite-monaco-diff.js";
|
|
4
|
+
import { CenterAndHeightResizer } from "../../CenterAndHeightResizer.js";
|
|
5
|
+
await customElements.whenDefined(MonacoDiffElement.tagName);
|
|
6
|
+
await customElements.whenDefined(CenterAndHeightResizer.tagName);
|
|
7
|
+
const diffEl = document.querySelector(MonacoDiffElement.tagName);
|
|
8
|
+
if (!(diffEl instanceof MonacoDiffElement)) {
|
|
9
|
+
throw new Error("Missing <composite-monaco-diff> element");
|
|
10
|
+
}
|
|
11
|
+
await diffEl.whenReady();
|
|
12
|
+
function wireResizerUrlSync(resizer, index) {
|
|
13
|
+
const config = {
|
|
14
|
+
left: {
|
|
15
|
+
default: resizer.getAttribute("left") ?? "100px",
|
|
16
|
+
getParam: "l",
|
|
17
|
+
encode: (value) => value,
|
|
18
|
+
decode: (value) => value,
|
|
19
|
+
},
|
|
20
|
+
center: {
|
|
21
|
+
default: resizer.getAttribute("center") ?? "1200px",
|
|
22
|
+
getParam: "c",
|
|
23
|
+
encode: (value) => value,
|
|
24
|
+
decode: (value) => value,
|
|
25
|
+
},
|
|
26
|
+
height: {
|
|
27
|
+
default: resizer.getAttribute("height") ?? "100px",
|
|
28
|
+
getParam: "h",
|
|
29
|
+
encode: (value) => value,
|
|
30
|
+
decode: (value) => value,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const { trackUrl } = modURLSearchParams(config, (key, i) => `${key}-${i}`);
|
|
34
|
+
const { setParams } = trackUrl((params, updatedURLSearchParams, governedKeys) => {
|
|
35
|
+
resizer.setAttribute("left", params.left);
|
|
36
|
+
resizer.setAttribute("center", params.center);
|
|
37
|
+
resizer.setAttribute("height", params.height);
|
|
38
|
+
const current = new URLSearchParams(window.location.search);
|
|
39
|
+
const next = syncURLSearchParams(current, governedKeys, updatedURLSearchParams);
|
|
40
|
+
if (next.toString() !== current.toString()) {
|
|
41
|
+
const url = buildUrlWithSearchParams(window.location.href, next);
|
|
42
|
+
history.replaceState(history.state, "", url);
|
|
43
|
+
}
|
|
44
|
+
}, { ctx: index, fireOnMount: true });
|
|
45
|
+
const syncToUrl = () => {
|
|
46
|
+
setParams({
|
|
47
|
+
left: resizer.getAttribute("left") ?? config.left.default,
|
|
48
|
+
center: resizer.getAttribute("center") ?? config.center.default,
|
|
49
|
+
height: resizer.getAttribute("height") ?? config.height.default,
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
resizer.addEventListener("onLeft", syncToUrl);
|
|
53
|
+
resizer.addEventListener("onCenter", syncToUrl);
|
|
54
|
+
resizer.addEventListener("onHeight", syncToUrl);
|
|
55
|
+
}
|
|
56
|
+
function applyThemeAttribute(diffEl, theme) {
|
|
57
|
+
if (theme) {
|
|
58
|
+
diffEl.setAttribute("theme", theme);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
diffEl.removeAttribute("theme");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function applyLanguageAttribute(diffEl, language) {
|
|
65
|
+
if (language) {
|
|
66
|
+
diffEl.setAttribute("language", language);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
diffEl.removeAttribute("language");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
document.querySelectorAll(CenterAndHeightResizer.tagName).forEach((el, index) => {
|
|
73
|
+
wireResizerUrlSync(el, index);
|
|
74
|
+
});
|
|
75
|
+
const themeSelect = document.getElementById("theme-select");
|
|
76
|
+
if (!(themeSelect instanceof HTMLSelectElement)) {
|
|
77
|
+
throw new Error("Missing #theme-select element");
|
|
78
|
+
}
|
|
79
|
+
const languageSelect = document.getElementById("language-select");
|
|
80
|
+
if (!(languageSelect instanceof HTMLSelectElement)) {
|
|
81
|
+
throw new Error("Missing #language-select element");
|
|
82
|
+
}
|
|
83
|
+
const { trackUrl: trackUrlNoIndex } = modURLSearchParams({
|
|
84
|
+
theme: {
|
|
85
|
+
default: "",
|
|
86
|
+
getParam: "theme",
|
|
87
|
+
encode: (value) => value,
|
|
88
|
+
decode: (value) => (isMonacoTheme(value) ? value : ""),
|
|
89
|
+
},
|
|
90
|
+
language: {
|
|
91
|
+
default: "javascript",
|
|
92
|
+
getParam: "lang",
|
|
93
|
+
encode: (value) => value,
|
|
94
|
+
decode: (value) => value,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
const { setParam } = trackUrlNoIndex((params, updatedURLSearchParams, governedKeys) => {
|
|
98
|
+
themeSelect.value = params.theme;
|
|
99
|
+
applyThemeAttribute(diffEl, params.theme);
|
|
100
|
+
languageSelect.value = params.language;
|
|
101
|
+
applyLanguageAttribute(diffEl, params.language);
|
|
102
|
+
const current = new URLSearchParams(window.location.search);
|
|
103
|
+
const next = syncURLSearchParams(current, governedKeys, updatedURLSearchParams);
|
|
104
|
+
if (next.toString() !== current.toString()) {
|
|
105
|
+
const url = buildUrlWithSearchParams(window.location.href, next);
|
|
106
|
+
history.replaceState(history.state, "", url);
|
|
107
|
+
}
|
|
108
|
+
}, { fireOnMount: true });
|
|
109
|
+
themeSelect.addEventListener("change", () => {
|
|
110
|
+
setParam("theme", themeSelect.value);
|
|
111
|
+
});
|
|
112
|
+
languageSelect.addEventListener("change", () => {
|
|
113
|
+
setParam("language", languageSelect.value);
|
|
114
|
+
});
|