Skip to content

Commit 296afac

Browse files
Merge branch 'dev'
2 parents fc6d773 + 6a11250 commit 296afac

31 files changed

Lines changed: 623 additions & 507 deletions

File tree

js/ai/prompts.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ Download the latest version of **Neurite Desktop** ↓
9797
${nodeTag} Unchain from the traditional limitations of a browser
9898
Forget tabs! Neurite is all about graphs. Open as many browser windows as you want and display them side by side in our infinite fractal canvas.
9999
The desktop release also includes a full web browsing experience inside link nodes — no more restrictions on which links you can open.
100-
- Forward and backward navigation built into each link node
100+
- Forward and backward navigation built into each link node
101101
- Create new link nodes by directly dragging out URLs
102-
- Works with any site, no sandboxing limitations
103-
- Automatic updates built-in
102+
- Works with any site, no sandboxing limitations
103+
- Automatic updates built-in
104104
105-
Neurite Desktop can be opened like any other app you've installed.
105+
Neurite Desktop can be opened like any other app you've installed.
106106
107107
Make sure to exclusivly reference the above described controls. Avoid divergence from the above how-to.`;
108108
}

js/globals.js

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,35 @@ var controls = {
116116
}
117117
};
118118

119-
var settings = {
119+
class Settings {
120+
#stored = new Stored('settings');
121+
constructor(){
122+
const defaults = Settings.default;
123+
for (const name in defaults) {
124+
Object.defineProperty(this, name, {
125+
get: this.get.bind(this, '_' + name),
126+
set: this.set.bind(this, name)
127+
})
128+
}
129+
130+
this.init();
131+
this.#stored.table.iterate(this.#set.bind(this));
132+
}
133+
134+
clear(){ this.#stored.clear() }
135+
init(){
136+
const defaults = Settings.default;
137+
for (const name in defaults) this.#set(defaults[name], name);
138+
}
139+
140+
get(key){ return this[key] }
141+
set(name, val){
142+
this.#stored.save(name, val);
143+
this.#set(val, name);
144+
}
145+
#set(val, name){ this['_' + name] = val }
146+
}
147+
Settings.default = {
120148
zoomSpeed: 0.001,
121149
dragZoomSpeed: 0.005,
122150
panSpeed: 1,
@@ -139,9 +167,12 @@ var settings = {
139167

140168
//slider adjustment
141169
maxLines: 128,
170+
renderLength: 50,
171+
renderQuality: 16,
142172
renderWidthMult: 0.3, //1,
143173
regenDebtAdjustmentFactor: 1,
144174

175+
framesDelay: 0,
145176
useDelayedRendering: true,
146177
renderDelay: 0,
147178
renderStepSize: 0.1, //0.25,
@@ -174,11 +205,12 @@ var settings = {
174205
innerOpacity: 1,
175206
outerOpacity: 1,
176207

177-
defaultSearchEngine: 'google' // Brave, Bing, DuckDuckGo, Google
178-
}
208+
defaultSearchEngine: 'google', // Brave, Bing, DuckDuckGo, Google
179209

180-
var flashlight_stdev = 0.25; // this is the radius of the flashlight
181-
var flashlight_fraction = 0.73; // this is what fraction of samples are diverted to the flashlight
210+
flashlight_stdev: 0.25, // radius of the flashlight
211+
flashlight_fraction: 0.73 // fraction of samples diverted to the flashlight
212+
}
213+
const settings = new Settings();
182214

183215

184216

@@ -266,7 +298,7 @@ class Tag {
266298
const input = e.currentTarget;
267299
const tag = input.value.trim();
268300
Tag[input.dataset.key] = (tag === '' ? ' ' : tag);
269-
301+
270302
ZettelkastenParser.regexpNodeTitle = RegExp.forNodeTitle(Tag.node);
271303
updateAllZetMirrorModes();
272304
updateAllZettelkastenProcessors();
@@ -544,10 +576,6 @@ Event.stopPropagationByNameForThis = function(eName){
544576
On[eName](this, Event.stopPropagation)
545577
}
546578

547-
function triggerInputEvent(elementId) {
548-
Elem.byId(elementId).dispatchEvent(new Event('input'))
549-
}
550-
551579
function clearTextSelections() {
552580
if (window.getSelection) {
553581
if (window.getSelection().empty) {

js/interface/dropdown/customui/rightclick/suggestions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
On.wheel(this.container, e => {
1313
e.stopPropagation();
1414
}, true);
15-
15+
1616
On.contextmenu(this.container, e => {
1717
e.preventDefault();
1818
e.stopPropagation();
1919
}, true);
20-
20+
2121
document.body.appendChild(this.container);
2222
}
2323
position(x, y) {

js/interface/dropdown/neuritepanel.js

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ window.NeuriteEnv = {
1414
if (this.isProd) return 'https://neurite.network';
1515
if (this.isTest) return 'https://test.neurite.network';
1616
return null; // Electron-local and dev-hosted must proxy
17+
},
18+
19+
isTrustedMessageOrigin(origin) {
20+
return (
21+
origin === 'https://neurite.network' ||
22+
origin === 'https://test.neurite.network' ||
23+
(this.isElectron && (origin === 'null' || origin.startsWith('http://localhost')))
24+
);
1725
}
1826
}
1927

@@ -45,7 +53,7 @@ class NeuriteBackend {
4553
},
4654
...options
4755
};
48-
56+
4957
try {
5058
const response = window.NeuriteEnv.isElectronWithLocalFrontend
5159
? await this.#electronRequest(endpoint, requestOptions, isStreaming)
@@ -75,25 +83,42 @@ class NeuriteBackend {
7583
if (handleUnauthorized) return response;
7684
return handleNeuriteUnauthorized();
7785
}
78-
7986
return response;
8087
}
8188

8289
async #electronRequest(endpoint, requestOptions, isStreaming) {
90+
const fullEndpoint = `/api${endpoint}`;
91+
const encoder = new TextEncoder();
92+
8393
if (isStreaming) {
8494
const id = crypto.randomUUID();
85-
const encoder = new TextEncoder();
86-
95+
96+
let responseHeaders = {};
97+
let status = 200;
98+
let statusText = '';
99+
87100
const stream = new ReadableStream({
88101
start(controller) {
89102
const onChunk = (event, data) => {
90103
if (data.id !== id) return;
91-
92-
if (data.chunk) controller.enqueue(encoder.encode(data.chunk));
104+
105+
if (data.streamMeta) {
106+
// Initial headers/status block
107+
responseHeaders = data.streamMeta.headers || {};
108+
status = data.streamMeta.status || 200;
109+
statusText = data.streamMeta.statusText || '';
110+
return;
111+
}
112+
113+
if (data.chunk) {
114+
controller.enqueue(encoder.encode(data.chunk));
115+
}
116+
93117
if (data.done) {
94118
controller.close();
95119
window.electronAPI._removeStreamListener(onChunk);
96120
}
121+
97122
if (data.error) {
98123
controller.error(new Error(data.error));
99124
window.electronAPI._removeStreamListener(onChunk);
@@ -102,26 +127,37 @@ class NeuriteBackend {
102127
window.electronAPI._addStreamListener(onChunk);
103128
}
104129
});
105-
106-
window.electronAPI.sendStreamRequest(id, `/api${endpoint}`, requestOptions);
107-
130+
131+
window.electronAPI.sendStreamRequest(id, fullEndpoint, requestOptions);
132+
108133
return new Response(stream, {
109-
headers: { 'Content-Type': 'application/json' }
134+
headers: new Headers(responseHeaders),
135+
status,
136+
statusText
110137
});
111138
}
112-
113-
const response = await window.electronAPI.secureFetch(endpoint, requestOptions);
114-
return {
115-
ok: response.ok,
116-
status: response.status,
117-
json: async () => response.data
118-
};
139+
140+
// --- Non-streaming ---
141+
const raw = await window.electronAPI.secureFetch(fullEndpoint, requestOptions);
142+
143+
const headers = new Headers(raw.headers || {});
144+
const body = raw.isText
145+
? raw.body
146+
: new Uint8Array(raw.body); // proxy.html sends byte array if not text
147+
148+
return new Response(
149+
raw.isText ? body : body.buffer,
150+
{
151+
status: raw.status,
152+
statusText: raw.statusText || '',
153+
headers
154+
}
155+
);
119156
}
120157
}
121158

122159
window.NeuriteBackend = new NeuriteBackend();
123160

124-
125161
// neuritePanel.js
126162

127163
class NeuritePanel {
@@ -347,9 +383,27 @@ class BalanceHandler {
347383
}
348384

349385
try {
350-
const paymentTab = window.open('about:blank', '_blank', 'width=500,height=600');
351386
const sessionId = await this.createCheckoutSession(roundedAmount);
352-
this.initPaymentTab(paymentTab, sessionId);
387+
const keys = await updatePublicKeys();
388+
const stripePublicKey = keys.stripePublicKey;
389+
390+
if (!stripePublicKey) throw new Error('Stripe public key not available');
391+
392+
const redirectPage = window.NeuriteEnv.isTest
393+
? 'https://test.neurite.network/resources/striperedirect.html'
394+
: 'https://neurite.network/resources/striperedirect.html';
395+
396+
const origin = window.location.origin;
397+
const stripeRedirectUrl = `${redirectPage}?sessionId=${encodeURIComponent(sessionId)}&pk=${encodeURIComponent(stripePublicKey)}&origin=${encodeURIComponent(origin)}`;
398+
399+
if (window.NeuriteEnv.isElectronWithLocalFrontend) {
400+
window.electronAPI.openPopupViaProxy(stripeRedirectUrl);
401+
} else {
402+
const popup = window.open(stripeRedirectUrl, 'StripePayment', 'width=500,height=600');
403+
if (!popup) {
404+
alert("Please enable popups for this site to complete the payment.");
405+
}
406+
}
353407
} catch (error) {
354408
// Error handling
355409
if (error.message.includes('Exceeds maximum balance')) {
@@ -361,35 +415,6 @@ class BalanceHandler {
361415
}
362416
}
363417

364-
// Method within your class to initialize the payment tab and load Stripe.js
365-
initPaymentTab(paymentTab, sessionId) {
366-
paymentTab.document.body.innerHTML = '<p>Redirecting to payment...</p>';
367-
const stripeScript = paymentTab.document.createElement('script');
368-
stripeScript.src = 'https://js.stripe.com/v3/';
369-
370-
stripeScript.onload = async () => {
371-
// Retrieve the Stripe key
372-
const keys = await updatePublicKeys();
373-
const stripePublicKey = keys.stripePublicKey;
374-
375-
if (stripePublicKey) {
376-
// Create and inject a script to initiate Stripe with the session ID
377-
const redirectScript = `
378-
const stripe = Stripe('${stripePublicKey}');
379-
stripe.redirectToCheckout({ sessionId: '${sessionId}' }).catch((error) => {
380-
document.body.innerHTML = 'Error: ' + error.message;
381-
});
382-
`;
383-
const scriptTag = paymentTab.document.createElement('script');
384-
scriptTag.text = redirectScript;
385-
paymentTab.document.body.appendChild(scriptTag);
386-
}
387-
};
388-
389-
// Append Stripe.js to the payment tab
390-
paymentTab.document.head.appendChild(stripeScript);
391-
}
392-
393418
// Fetch the user's balance directly from the Stripe Worker
394419
async fetchUserBalance() {
395420
try {

js/interface/dropdown/savenet.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class GraphsKeeper {
1414

1515
deleteBlob(blobId){ return this.#blobData.delete(blobId) }
1616
deleteBlobMeta(graphId){ return this.#blobMeta.delete(graphId) }
17-
#deleteBlobs = (dictMeta)=>{
17+
#deleteBlobs = (dictMeta)=>{
1818
for (const blobId in dictMeta) this.deleteBlob(blobId)
1919
}
2020
deleteForMeta(meta){
@@ -435,6 +435,11 @@ View.Graphs = class {
435435
this.#btnClear.text = "Clear";
436436
}
437437

438+
#onBtnResetSettingsClicked(e){
439+
settings.clear();
440+
settings.init();
441+
editTab.init();
442+
}
438443
#onBtnClearLocalClicked = (e)=>{
439444
localStorage.clear();
440445
Stored.drop('Neurite');
@@ -834,6 +839,7 @@ View.Graphs = class {
834839
On.click(this.#btnClear, this.#onBtnClearClicked);
835840
On.click(this.#btnClearSure, this.#onBtnClearSureClicked);
836841
On.click(this.#btnClearUnsure, this.#onBtnClearUnsureClicked);
842+
On.click(Elem.byId('resetSettings'), this.#onBtnResetSettingsClicked);
837843
On.click(Elem.byId('clearLocalStorage'), this.#onBtnClearLocalClicked);
838844
On.click(Elem.byId('new-save-button'), (e)=>this.#saver.save() );
839845

js/interface/dropdown/signin.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
window.addEventListener('message', async function (event) {
2-
const trustedOrigin = 'https://neurite.network' || 'https://test.neurite.network';
3-
if (event.origin !== trustedOrigin) {
4-
Logger.warn('Invalid origin from redirect:', event.origin);
2+
const data = event.data;
3+
const origin = event.origin;
4+
console.log('[frontend] Received postMessage:', origin, data);
5+
6+
if (!window.NeuriteEnv.isTrustedMessageOrigin(origin)) {
7+
Logger.warn('Invalid origin from redirect:', origin);
58
return;
69
}
710

8-
const { type, email, status, error } = event.data;
11+
const { type, email, status, error } = data;
912

10-
if (type === 'auth') {
11-
if (email) {
12-
updateSignInState(email);
13-
neuritePanel.open(true);
14-
}
13+
if (type === 'auth' && email) {
14+
updateSignInState(email);
15+
neuritePanel.open(true);
1516
} else if (type === 'stripe') {
1617
if (status === 'success') {
1718
neuritePanel.open(true);
@@ -64,15 +65,19 @@ async function signIn() {
6465
const redirectPage = window.NeuriteEnv.isTest
6566
? 'https://test.neurite.network/resources/verify.html'
6667
: 'https://neurite.network/resources/verify.html';
67-
68+
6869
const workerUrl = `${backendBase}/api/oauth`;
6970
const frontendOrigin = window.location.origin;
7071

71-
const popupUrl = `${redirectPage}?workerUrl=${encodeURIComponent(workerUrl)}&siteKey=${encodeURIComponent(turnstilePublicKey)}&origin=${encodeURIComponent(frontendOrigin)}`;
72-
const popup = window.open(popupUrl, 'Verification', 'width=500,height=600');
72+
const popupUrl = `${redirectPage}?workerUrl=${encodeURIComponent(workerUrl)}&siteKey=${encodeURIComponent(turnstilePublicKey)}&origin=${encodeURIComponent(frontendOrigin)}`;
7373

74-
if (!popup) {
75-
alert("Please enable popups for this site to complete the verification.");
74+
if (window.NeuriteEnv.isElectronWithLocalFrontend) {
75+
window.electronAPI.openPopupViaProxy(popupUrl);
76+
} else {
77+
const popup = window.open(popupUrl, 'Verification', 'width=500,height=600');
78+
if (!popup) {
79+
alert("Please enable popups for this site to complete the verification.");
80+
}
7681
}
7782
} catch (error) {
7883
Logger.err("Sign-in failed:", error);
@@ -187,4 +192,4 @@ function handleNeuriteUnauthorized() {
187192
console.error("Failed to display confirm dialog:", error);
188193
throw error; // Re-throw the error to maintain control flow
189194
});
190-
}
195+
}

0 commit comments

Comments
 (0)