desktop: components/apps/browser — simple web browser with URL bar, navigation, iframe sandbox
This commit is contained in:
parent
6beb93b16f
commit
eea8197ece
@ -0,0 +1,29 @@
|
|||||||
|
<div class="browser">
|
||||||
|
<!-- Navigation bar -->
|
||||||
|
<div class="browser-toolbar">
|
||||||
|
<button class="nav-btn" (click)="refresh()" title="Refresh">↻</button>
|
||||||
|
<div class="url-bar">
|
||||||
|
<input
|
||||||
|
class="url-input"
|
||||||
|
type="text"
|
||||||
|
[value]="inputUrl()"
|
||||||
|
(keydown)="onUrlKeydown($event)"
|
||||||
|
placeholder="Enter URL…"
|
||||||
|
/>
|
||||||
|
<button class="go-btn" (click)="navigate()">Go</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div class="browser-content">
|
||||||
|
<iframe
|
||||||
|
[src]="url()"
|
||||||
|
class="browser-frame"
|
||||||
|
sandbox="allow-same-origin allow-scripts allow-forms"
|
||||||
|
(load)="onLoad()"
|
||||||
|
></iframe>
|
||||||
|
@if (isLoading()) {
|
||||||
|
<div class="browser-loading">Loading…</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: #2d2d2d;
|
||||||
|
border-bottom: 1px solid #404040;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
width: 32px;
|
||||||
|
height: 30px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-bar {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #383838;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: #0078d4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.go-btn {
|
||||||
|
width: 50px;
|
||||||
|
border: none;
|
||||||
|
background: #0078d4;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #1a86d9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-content {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-frame {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: #888;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
40
desktop/src/app/components/apps/browser/browser.component.ts
Normal file
40
desktop/src/app/components/apps/browser/browser.component.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Component, signal } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-browser',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
templateUrl: './browser.component.html',
|
||||||
|
styleUrl: './browser.component.scss',
|
||||||
|
})
|
||||||
|
export class BrowserComponent {
|
||||||
|
readonly url = signal('https://www.wikipedia.org');
|
||||||
|
readonly inputUrl = signal('https://www.wikipedia.org');
|
||||||
|
readonly isLoading = signal(false);
|
||||||
|
|
||||||
|
navigate(): void {
|
||||||
|
let target = this.inputUrl().trim();
|
||||||
|
if (!target.startsWith('http://') && !target.startsWith('https://')) {
|
||||||
|
target = 'https://' + target;
|
||||||
|
}
|
||||||
|
this.inputUrl.set(target);
|
||||||
|
this.url.set(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUrlKeydown(event: KeyboardEvent): void {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
this.navigate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(): void {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh(): void {
|
||||||
|
this.isLoading.set(true);
|
||||||
|
this.url.set(this.url() + ' '); // Force iframe reload
|
||||||
|
setTimeout(() => this.url.set(this.inputUrl()), 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user