Skip to content

Commit 5af5b84

Browse files
committed
[frontend] refactor in preparation for history emulation
1 parent 1ff5dcf commit 5af5b84

File tree

5 files changed

+190
-182
lines changed

5 files changed

+190
-182
lines changed

frontend/src/StatefulClass.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { Stateful } from "dreamland/core";
2+
3+
export class StatefulClass {
4+
constructor(state: Stateful<any>) {
5+
return state;
6+
}
7+
}

frontend/src/Tab.ts

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { createState } from "dreamland/core";
2+
import { StatefulClass } from "./StatefulClass";
3+
import { scramjet } from "./main";
4+
import type { HistoryState } from "./history";
5+
6+
let id = 0;
7+
export class Tab extends StatefulClass {
8+
id: number;
9+
title: string | null;
10+
frame: ScramjetFrame;
11+
url: string;
12+
13+
dragoffset: number;
14+
dragpos: number;
15+
startdragpos: number;
16+
17+
width: number;
18+
pos: number;
19+
icon: string;
20+
21+
history: HistoryState[];
22+
23+
constructor() {
24+
super(createState({}));
25+
this.id = id++;
26+
27+
this.frame = scramjet.createFrame();
28+
this.title = null;
29+
this.url = "puter://blank";
30+
this.icon = "/vite.svg";
31+
32+
this.dragoffset = -1;
33+
this.startdragpos = -1;
34+
this.dragpos = -1;
35+
this.width = 0;
36+
this.pos = 0;
37+
this.history = [];
38+
}
39+
}
40+
41+
// frame.addEventListener("navigate", (e) => {
42+
// console.error(e);
43+
// tab.url = frame.client.url.href;
44+
// });
45+
// frame.frame.addEventListener("load", (e) => {
46+
// tab.url = frame.client.url.href;
47+
// });
48+
// let browser = this;
49+
50+
// frame.addEventListener("contextInit", (ctx) => {
51+
// ctx.client.Proxy("window.open", {
52+
// apply(pctx) {
53+
// let tab = browser.newTab("_blank");
54+
// tab.frame.go(pctx.args[0]);
55+
56+
// // this is a bit jank: we need the global proxy NOW, but it will only load naturally after a few seconds
57+
// const realContentWindow = tab.frame.frame.contentWindow;
58+
// // :(
59+
// const ctor: any = ctx.client.constructor;
60+
// // see open.ts
61+
// const newclient = new ctor(realContentWindow);
62+
// newclient.hook();
63+
64+
// pctx.return(newclient.globalProxy);
65+
// },
66+
// });
67+
68+
// const framedoc = ctx.window.document;
69+
70+
// framedoc.addEventListener("contextmenu", (e) => {
71+
// // need to calculate the real position of the frame relative to the top
72+
// let xoff = 0;
73+
// let yoff = 0;
74+
// let currentwin = ctx.window;
75+
// while (currentwin.parent && currentwin.frameElement) {
76+
// // this will return true until the end of the scramjet boundary
77+
// let { x, y } = currentwin.frameElement.getBoundingClientRect();
78+
// xoff += x;
79+
// yoff += y;
80+
// currentwin = currentwin.parent;
81+
// }
82+
// // parent is trapped, so it won't calculate the topmost iframe. do that manually
83+
// let { x, y } = frame.frame.getBoundingClientRect();
84+
// xoff += x;
85+
// yoff += y;
86+
// createMenu(xoff + e.pageX, yoff + e.pageY, [
87+
// {
88+
// label: "Back",
89+
// action: () => {
90+
// frame.back();
91+
// },
92+
// },
93+
// {
94+
// label: "Forward",
95+
// action: () => {
96+
// frame.forward();
97+
// },
98+
// },
99+
// {
100+
// label: "Reload",
101+
// action: () => {
102+
// frame.reload();
103+
// },
104+
// },
105+
// {
106+
// label: "Bookmark",
107+
// action: () => {
108+
// console.log("Bookmarking", tab.title, tab.url);
109+
// },
110+
// },
111+
// ]);
112+
// e.preventDefault();
113+
// });
114+
// const head = framedoc.querySelector("head")!;
115+
// const observer = new MutationObserver(() => {
116+
// const title = framedoc.querySelector("title");
117+
// if (title) {
118+
// tab.title = title.textContent || "New Tab";
119+
// } else {
120+
// tab.title = "New Tab";
121+
// }
122+
// const favicon = framedoc.querySelector(
123+
// "link[rel='icon'], link[rel='shortcut icon']"
124+
// );
125+
// if (favicon) {
126+
// const iconhref = favicon.getAttribute("href");
127+
// if (iconhref) {
128+
// const rewritten = scramjet.encodeUrl(
129+
// new URL(iconhref, frame.client.url)
130+
// );
131+
// tab.icon = rewritten;
132+
// } else {
133+
// tab.icon = "/vite.svg";
134+
// }
135+
// } else {
136+
// tab.icon = scramjet.encodeUrl(
137+
// new URL("/favicon.ico", frame.client.url)
138+
// );
139+
// }
140+
// });
141+
// observer.observe(head, {
142+
// childList: true,
143+
// subtree: true,
144+
// });
145+
// const anchorObserver = new MutationObserver((mutations) => {
146+
// mutations.forEach((mutation) => {
147+
// mutation.addedNodes.forEach((node) => {
148+
// if (node instanceof HTMLAnchorElement) {
149+
// const openInNewTab = () => {
150+
// const href = scramjet.decodeUrl(node.href);
151+
// let newtab = this.newTab("title");
152+
// if (href) {
153+
// newtab.frame.go(href);
154+
// } else {
155+
// newtab.frame.go("about:blank");
156+
// }
157+
// };
158+
// node.addEventListener("click", (e) => {
159+
// if (node.target !== "_blank") return;
160+
// e.preventDefault();
161+
// openInNewTab();
162+
// });
163+
// node.addEventListener("auxclick", (e) => {
164+
// if (e.button !== 1) return; // middle click
165+
// e.preventDefault();
166+
// openInNewTab();
167+
// });
168+
// }
169+
// });
170+
// });
171+
// });
172+
// anchorObserver.observe(framedoc, {
173+
// childList: true,
174+
// subtree: true,
175+
// });
176+
// });
177+
// use(tab.url).listen(() => {
178+
// this.activetab = this.activetab;
179+
// });

frontend/src/browser.tsx

Lines changed: 2 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,11 @@ import iconAdd from "@ktibow/iconset-ion/add";
77
import { Shell } from "./components/Shell";
88
import { createMenu } from "./components/Menu";
99
import { createDelegate } from "./delegate";
10+
import { StatefulClass } from "./StatefulClass";
1011

1112
export const pushTab = createDelegate<Tab>();
1213
export const popTab = createDelegate<Tab>();
1314

14-
class StatefulClass {
15-
constructor(state: Stateful<any>) {
16-
return state;
17-
}
18-
}
19-
2015
export class Browser extends StatefulClass {
2116
built: boolean = false;
2217

@@ -53,148 +48,7 @@ export class Browser extends StatefulClass {
5348
}
5449

5550
newTab(title: string) {
56-
let frame = scramjet.createFrame();
57-
let tab = new Tab(title, frame);
58-
frame.go("https://google.com");
59-
frame.addEventListener("navigate", (e) => {
60-
console.error(e);
61-
tab.url = frame.client.url.href;
62-
});
63-
frame.frame.addEventListener("load", (e) => {
64-
tab.url = frame.client.url.href;
65-
});
66-
let browser = this;
67-
68-
frame.addEventListener("contextInit", (ctx) => {
69-
ctx.client.Proxy("window.open", {
70-
apply(pctx) {
71-
let tab = browser.newTab("_blank");
72-
tab.frame.go(pctx.args[0]);
73-
74-
// this is a bit jank: we need the global proxy NOW, but it will only load naturally after a few seconds
75-
const realContentWindow = tab.frame.frame.contentWindow;
76-
// :(
77-
const ctor: any = ctx.client.constructor;
78-
// see open.ts
79-
const newclient = new ctor(realContentWindow);
80-
newclient.hook();
81-
82-
pctx.return(newclient.globalProxy);
83-
},
84-
});
85-
86-
const framedoc = ctx.window.document;
87-
88-
framedoc.addEventListener("contextmenu", (e) => {
89-
// need to calculate the real position of the frame relative to the top
90-
let xoff = 0;
91-
let yoff = 0;
92-
let currentwin = ctx.window;
93-
while (currentwin.parent && currentwin.frameElement) {
94-
// this will return true until the end of the scramjet boundary
95-
let { x, y } = currentwin.frameElement.getBoundingClientRect();
96-
xoff += x;
97-
yoff += y;
98-
currentwin = currentwin.parent;
99-
}
100-
// parent is trapped, so it won't calculate the topmost iframe. do that manually
101-
let { x, y } = frame.frame.getBoundingClientRect();
102-
xoff += x;
103-
yoff += y;
104-
createMenu(xoff + e.pageX, yoff + e.pageY, [
105-
{
106-
label: "Back",
107-
action: () => {
108-
frame.back();
109-
},
110-
},
111-
{
112-
label: "Forward",
113-
action: () => {
114-
frame.forward();
115-
},
116-
},
117-
{
118-
label: "Reload",
119-
action: () => {
120-
frame.reload();
121-
},
122-
},
123-
{
124-
label: "Bookmark",
125-
action: () => {
126-
console.log("Bookmarking", tab.title, tab.url);
127-
},
128-
},
129-
]);
130-
e.preventDefault();
131-
});
132-
const head = framedoc.querySelector("head")!;
133-
const observer = new MutationObserver(() => {
134-
const title = framedoc.querySelector("title");
135-
if (title) {
136-
tab.title = title.textContent || "New Tab";
137-
} else {
138-
tab.title = "New Tab";
139-
}
140-
const favicon = framedoc.querySelector(
141-
"link[rel='icon'], link[rel='shortcut icon']"
142-
);
143-
if (favicon) {
144-
const iconhref = favicon.getAttribute("href");
145-
if (iconhref) {
146-
const rewritten = scramjet.encodeUrl(
147-
new URL(iconhref, frame.client.url)
148-
);
149-
tab.icon = rewritten;
150-
} else {
151-
tab.icon = "/vite.svg";
152-
}
153-
} else {
154-
tab.icon = scramjet.encodeUrl(
155-
new URL("/favicon.ico", frame.client.url)
156-
);
157-
}
158-
});
159-
observer.observe(head, {
160-
childList: true,
161-
subtree: true,
162-
});
163-
const anchorObserver = new MutationObserver((mutations) => {
164-
mutations.forEach((mutation) => {
165-
mutation.addedNodes.forEach((node) => {
166-
if (node instanceof HTMLAnchorElement) {
167-
const openInNewTab = () => {
168-
const href = scramjet.decodeUrl(node.href);
169-
let newtab = this.newTab("title");
170-
if (href) {
171-
newtab.frame.go(href);
172-
} else {
173-
newtab.frame.go("about:blank");
174-
}
175-
};
176-
node.addEventListener("click", (e) => {
177-
if (node.target !== "_blank") return;
178-
e.preventDefault();
179-
openInNewTab();
180-
});
181-
node.addEventListener("auxclick", (e) => {
182-
if (e.button !== 1) return; // middle click
183-
e.preventDefault();
184-
openInNewTab();
185-
});
186-
}
187-
});
188-
});
189-
});
190-
anchorObserver.observe(framedoc, {
191-
childList: true,
192-
subtree: true,
193-
});
194-
});
195-
use(tab.url).listen(() => {
196-
this.activetab = this.activetab;
197-
});
51+
let tab = new Tab();
19852
pushTab(tab);
19953
this.tabs = [...this.tabs, tab];
20054
this.activetab = tab;

0 commit comments

Comments
 (0)