Skip to content

Commit 31e80de

Browse files
committed
[frontend] add puter://history page
1 parent 17004b4 commit 31e80de

File tree

9 files changed

+105
-38
lines changed

9 files changed

+105
-38
lines changed

frontend/src/Browser.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class Browser extends StatefulClass {
5656
tabs: Tab[] = [];
5757
activetab: Tab;
5858

59-
globalhistory: HistoryState[];
59+
globalhistory: HistoryState[] = [];
6060

6161
unfocusframes: boolean = false;
6262

@@ -82,8 +82,7 @@ export class Browser extends StatefulClass {
8282
let tab = this.newTab();
8383
tab.deserialize(detab);
8484
}
85-
this.activetab = this.tabs[0]; // TODO
86-
// this.activetab = this.tabs.find((t) => t.id == de.activetab)!;
85+
this.activetab = this.tabs.find((t) => t.id == de.activetab)!;
8786
console.log(this.activetab, this.activetab.url);
8887
}
8988

frontend/src/History.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,39 @@ import type { Tab } from "./Tab";
44
// history api emulation
55
export class HistoryState {
66
url: URL;
7-
tab: Tab;
87
state: any;
9-
title?: string;
10-
favicon?: string;
8+
title: string | null;
9+
favicon: string | null;
10+
timestamp: number;
1111

1212
constructor(partial?: Partial<HistoryState>) {
1313
Object.assign(this, partial);
14+
this.timestamp = Date.now();
1415
}
1516

1617
serialize(): SerializedHistoryState {
1718
return {
1819
state: this.state,
1920
url: this.url.href,
20-
tab: this.tab.id,
2121
title: this.title,
2222
favicon: this.favicon,
23+
timestamp: this.timestamp,
2324
};
2425
}
2526
deserialize(de: SerializedHistoryState) {
2627
this.state = de.state;
2728
this.url = new URL(de.url);
28-
this.tab = browser.tabs.find((t) => t.id === de.tab) || this.tab;
2929
this.title = de.title;
3030
this.favicon = de.favicon;
31+
this.timestamp = de.timestamp;
3132
}
3233
}
3334
export type SerializedHistoryState = {
3435
state: any;
3536
url: string;
36-
tab: number;
37-
title?: string;
38-
favicon?: string;
37+
title: string | null;
38+
favicon: string | null;
39+
timestamp: number;
3940
};
4041

4142
export type SerializedHistory = {
@@ -73,8 +74,8 @@ export class History {
7374
}
7475

7576
push(url: URL, state: any = null, navigate: boolean = true): HistoryState {
76-
const hstate = new HistoryState({ url, state, tab: this.tab });
77-
browser.globalhistory.push(hstate);
77+
const hstate = new HistoryState({ url, state });
78+
if (url.href != "puter://newtab") browser.globalhistory.push(hstate);
7879
this.states.push(hstate);
7980
this.index++;
8081

@@ -89,9 +90,8 @@ export class History {
8990
if (this.index < this.states.length) {
9091
this.current().url = url;
9192
this.current().state = state;
92-
this.current().tab = this.tab;
93-
this.current().title = undefined;
94-
this.current().favicon = undefined;
93+
this.current().title = null;
94+
this.current().favicon = null;
9595
} else {
9696
return this.push(url, state);
9797
}

frontend/src/Tab.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import {
77
injectHistoryEmulation,
88
type SerializedHistory,
99
} from "./History";
10-
import { NewTab } from "./pages/NewTab";
11-
import { Playground } from "./pages/Playground";
10+
import { NewTabPage } from "./pages/NewTabPage";
11+
import { PlaygroundPage } from "./pages/PlaygroundPage";
1212
import { createMenu } from "./components/Menu";
13-
import { About } from "./pages/About";
13+
import { AboutPage } from "./pages/AboutPage";
14+
import { HistoryPage } from "./pages/HistoryPage";
1415

1516
const requestInspectElement = createDelegate<[HTMLElement, Tab]>();
1617

@@ -106,15 +107,19 @@ export class Tab extends StatefulClass {
106107
switch (url.host) {
107108
case "newtab":
108109
this.title = "New Tab";
109-
this.internalpage = <NewTab tab={this} />;
110+
this.internalpage = <NewTabPage tab={this} />;
110111
break;
111112
case "playground":
112113
this.title = "Scramjet Playground";
113-
this.internalpage = <Playground tab={this} />;
114+
this.internalpage = <PlaygroundPage tab={this} />;
115+
break;
116+
case "history":
117+
this.title = "Browser History";
118+
this.internalpage = <HistoryPage tab={this}></HistoryPage>;
114119
break;
115120
case "version":
116121
this.title = "About Version";
117-
this.internalpage = <About tab={this} />;
122+
this.internalpage = <AboutPage tab={this} />;
118123
}
119124
} else {
120125
this.internalpage = null;
@@ -431,6 +436,8 @@ function injectTitleWatcher(client: ScramjetClient, tab: Tab) {
431436
} else {
432437
tab.icon = scramjet.encodeUrl(new URL("/favicon.ico", client.url));
433438
}
439+
tab.history.current().title = tab.title;
440+
tab.history.current().favicon = tab.icon;
434441
});
435442
observer.observe(head, {
436443
childList: true,

frontend/src/components/Omnibox.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export const UrlInput: Component<
152152
this.active = false;
153153
this.input.blur();
154154
} else {
155-
this.navigate(this.value);
155+
browser.searchNavigate(this.value);
156156
}
157157
}
158158
}}
@@ -297,8 +297,7 @@ export const Omnibox: Component<{
297297
{
298298
label: "History",
299299
action: () => {
300-
let t = browser.newTab();
301-
t.replaceNavigate(new URL("puter://history"));
300+
let t = browser.newTab(new URL("puter://history"));
302301
},
303302
},
304303
{

frontend/src/components/Shell.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ export const Shell: Component = function (cx) {
6363
if (!container) throw new Error(`No container found for tab ${tab.id}`);
6464
container.remove();
6565
});
66-
forceScreenshot.listen(async (tab) => {
67-
const container = cx.root.querySelector(
68-
`[data-tab="${tab.id}"]`
69-
) as HTMLElement;
70-
if (!container) throw new Error(`No container found for tab ${tab.id}`);
66+
// forceScreenshot.listen(async (tab) => {
67+
// const container = cx.root.querySelector(
68+
// `[data-tab="${tab.id}"]`
69+
// ) as HTMLElement;
70+
// if (!container) throw new Error(`No container found for tab ${tab.id}`);
7171

72-
// tab.screenshot = URL.createObjectURL(await toBlob(container));
73-
});
72+
// // tab.screenshot = URL.createObjectURL(await toBlob(container));
73+
// });
7474

7575
return <div></div>;
7676
};

frontend/src/pages/About.tsx renamed to frontend/src/pages/AboutPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { css, type Component } from "dreamland/core";
22
import type { Tab } from "../Tab";
33
import { scramjet } from "../main";
44

5-
export const About: Component<
5+
export const AboutPage: Component<
66
{
77
tab: Tab;
88
},
@@ -17,7 +17,7 @@ export const About: Component<
1717
</div>
1818
);
1919
};
20-
About.style = css`
20+
AboutPage.style = css`
2121
:scope {
2222
width: 100%;
2323
height: 100%;

frontend/src/pages/HistoryPage.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { css, type Component } from "dreamland/core";
2+
import type { Tab } from "../Tab";
3+
import { browser } from "../main";
4+
5+
export const HistoryPage: Component<
6+
{
7+
tab: Tab;
8+
},
9+
{}
10+
> = function (cx) {
11+
return (
12+
<div>
13+
<h1>History</h1>
14+
<div class="entries">
15+
{browser.globalhistory
16+
.sort((a, b) => b.timestamp - a.timestamp)
17+
.map((entry) => (
18+
<div
19+
class="entry"
20+
on:click={() => {
21+
browser.newTab(entry.url);
22+
}}
23+
>
24+
<img src={entry.favicon || "/vite.svg"} alt="favicon" />
25+
<span class="title">{entry.title || entry.url.href}</span>
26+
<span class="url">{entry.url.hostname}</span>
27+
</div>
28+
))}
29+
</div>
30+
</div>
31+
);
32+
};
33+
HistoryPage.style = css`
34+
:scope {
35+
width: 100%;
36+
height: 100%;
37+
display: flex;
38+
flex-direction: column;
39+
align-items: center;
40+
font-family: sans-serif;
41+
}
42+
.entries {
43+
display: flex;
44+
flex-direction: column;
45+
gap: 1em;
46+
width: 70%;
47+
}
48+
.entry {
49+
display: flex;
50+
align-items: center;
51+
/*border-bottom: 1px solid #ccc;*/
52+
cursor: pointer;
53+
gap: 0.5em;
54+
}
55+
.entry img {
56+
width: 16px;
57+
height: 16px;
58+
}
59+
.entry .title {
60+
font-weight: bold;
61+
}
62+
`;

frontend/src/pages/NewTab.tsx renamed to frontend/src/pages/NewTabPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { css, type Component } from "dreamland/core";
22
import type { Tab } from "../Tab";
33
import { browser } from "../main";
44

5-
export const NewTab: Component<
5+
export const NewTabPage: Component<
66
{
77
tab: Tab;
88
},
@@ -25,7 +25,7 @@ export const NewTab: Component<
2525
</div>
2626
);
2727
};
28-
NewTab.style = css`
28+
NewTabPage.style = css`
2929
:scope {
3030
width: 100%;
3131
height: 100%;

frontend/src/pages/Playground.tsx renamed to frontend/src/pages/PlaygroundPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Editor.style = css`
2828
}
2929
`;
3030

31-
export const Playground: Component<
31+
export const PlaygroundPage: Component<
3232
{
3333
tab: Tab;
3434
},
@@ -41,7 +41,7 @@ export const Playground: Component<
4141
</div>
4242
);
4343
};
44-
Playground.style = css`
44+
PlaygroundPage.style = css`
4545
:scope {
4646
width: 100%;
4747
height: 100%;

0 commit comments

Comments
 (0)