gui/pkg/display/preload.go
Snider 2c59364250
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
Implement chat, preload shims, and smart layouts
2026-04-15 13:39:13 +01:00

164 lines
5.2 KiB
Go

package display
import (
"strings"
core "dappco.re/go/core"
)
type PreloadTarget interface {
ExecJS(string)
}
func (s *Service) InjectPreload(webview PreloadTarget, origin string) error {
script, err := s.BuildPreloadScript(origin)
if err != nil {
return err
}
if strings.TrimSpace(script) == "" {
return nil
}
webview.ExecJS(script)
return nil
}
func (s *Service) BuildPreloadScript(rawURL string) (string, error) {
parts := []string{
s.injectStoragePolyfills(rawURL),
s.injectElectronShim(),
s.injectCoreMLShim(),
s.injectAppPreloads(rawURL),
}
return strings.Join(parts, "\n"), nil
}
func (s *Service) injectStoragePolyfills(origin string) string {
return `(function() {
const __coreOrigin = ` + core.JSONMarshalString(origin) + `;
const __coreScopes = globalThis.__coreStorageScopes || (globalThis.__coreStorageScopes = {});
const __scope = __coreScopes[__coreOrigin] || (__coreScopes[__coreOrigin] = { local: {}, session: {} });
const createStorage = (bucket) => ({
getItem(key) { return Object.prototype.hasOwnProperty.call(bucket, key) ? String(bucket[key]) : null; },
setItem(key, value) { bucket[key] = String(value); },
removeItem(key) { delete bucket[key]; },
clear() { Object.keys(bucket).forEach((key) => delete bucket[key]); },
key(index) { return Object.keys(bucket)[index] ?? null; },
get length() { return Object.keys(bucket).length; }
});
globalThis.core = globalThis.core || {};
globalThis.core.storage = globalThis.core.storage || {};
globalThis.core.storage.local = createStorage(__scope.local);
globalThis.core.storage.session = createStorage(__scope.session);
try {
if (!globalThis.localStorage) {
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
enumerable: true,
get() { return globalThis.core.storage.local; }
});
}
} catch (_) {}
try {
if (!globalThis.sessionStorage) {
Object.defineProperty(globalThis, 'sessionStorage', {
configurable: true,
enumerable: true,
get() { return globalThis.core.storage.session; }
});
}
} catch (_) {}
})();`
}
func (s *Service) injectElectronShim() string {
return `(function() {
if (globalThis.electron) {
return;
}
const listeners = new Map();
const toEventName = (channel) => "__core_electron__:" + channel;
const ipcRenderer = {
send(channel, ...args) {
globalThis.dispatchEvent(new CustomEvent(toEventName(channel), { detail: args }));
},
invoke(channel, ...args) {
globalThis.dispatchEvent(new CustomEvent(toEventName(channel), { detail: args }));
return Promise.resolve({ channel, args });
},
on(channel, listener) {
const handler = (event) => listener(event, ...(event.detail || []));
listeners.set(listener, handler);
globalThis.addEventListener(toEventName(channel), handler);
return () => ipcRenderer.removeListener(channel, listener);
},
once(channel, listener) {
const off = ipcRenderer.on(channel, (event, ...args) => {
off();
listener(event, ...args);
});
return off;
},
removeListener(channel, listener) {
const handler = listeners.get(listener);
if (handler) {
globalThis.removeEventListener(toEventName(channel), handler);
listeners.delete(listener);
}
}
};
const shell = {
openExternal(url) {
globalThis.open?.(url, "_blank", "noopener,noreferrer");
return Promise.resolve();
},
openPath(path) {
globalThis.location.href = path;
return Promise.resolve("");
}
};
const clipboard = {
readText() {
return globalThis.navigator?.clipboard?.readText?.() ?? Promise.resolve("");
},
writeText(text) {
return globalThis.navigator?.clipboard?.writeText?.(text) ?? Promise.resolve();
}
};
globalThis.electron = { ipcRenderer, shell, clipboard };
globalThis.require = (name) => name === "electron" ? globalThis.electron : undefined;
})();`
}
func (s *Service) injectCoreMLShim() string {
return `(function() {
const __coreApiURL = ` + core.JSONMarshalString(strings.TrimRight(core.Env("CORE_ML_API_URL"), "/")) + ` || "http://localhost:8090";
globalThis.core = globalThis.core || {};
globalThis.core.ml = globalThis.core.ml || {
async generate(input) {
const payload = typeof input === "string"
? { messages: [{ role: "user", content: input }], stream: false }
: { ...input, stream: false };
const response = await fetch((__coreApiURL || "http://localhost:8090") + "/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
return response.json();
},
async stream(input) {
const payload = typeof input === "string"
? { messages: [{ role: "user", content: input }], stream: true }
: { ...input, stream: true };
return fetch((__coreApiURL || "http://localhost:8090") + "/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
}
};
})();`
}
func (s *Service) injectAppPreloads(_ string) string {
return ""
}