Initial commit
This commit is contained in:
0
src/app/app.component.css
Normal file
0
src/app/app.component.css
Normal file
12
src/app/app.component.html
Normal file
12
src/app/app.component.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<mat-toolbar><span>BufPiv</span></mat-toolbar>
|
||||
<mat-sidenav-container>
|
||||
<mat-sidenav>
|
||||
<app-file-tree [files]="files()"></app-file-tree>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content>
|
||||
@if (!selectedDirectory) {
|
||||
<button mat-button (click)="selectDirectory()">Open Folder</button>
|
||||
}@else { }
|
||||
</mat-sidenav-content>
|
||||
<!-- TODO: Proto selector (add/remove proto definitinos, select definition for current file) on right side -->
|
||||
</mat-sidenav-container>
|
||||
59
src/app/app.component.ts
Normal file
59
src/app/app.component.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, signal } from "@angular/core";
|
||||
import { MatButtonModule } from "@angular/material/button";
|
||||
import { MatSidenavModule } from "@angular/material/sidenav";
|
||||
import { RouterOutlet } from "@angular/router";
|
||||
import { message, open } from "@tauri-apps/api/dialog";
|
||||
import { FileEntry, readDir } from "@tauri-apps/api/fs";
|
||||
import { MatToolbarModule } from "@angular/material/toolbar";
|
||||
import { MatTreeModule } from "@angular/material/tree";
|
||||
import {
|
||||
FileOrFolder,
|
||||
FileTreeComponent,
|
||||
} from "./file-tree/file-tree.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-root",
|
||||
standalone: true,
|
||||
templateUrl: "./app.component.html",
|
||||
styleUrl: "./app.component.css",
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterOutlet,
|
||||
MatSidenavModule,
|
||||
MatButtonModule,
|
||||
MatToolbarModule,
|
||||
MatTreeModule,
|
||||
FileTreeComponent,
|
||||
],
|
||||
})
|
||||
export class AppComponent {
|
||||
protected selectedDirectory: string | null = null;
|
||||
protected files = signal<FileOrFolder[]>([]);
|
||||
|
||||
async selectDirectory() {
|
||||
const selectedDirectory = await open({
|
||||
directory: true,
|
||||
multiple: false,
|
||||
});
|
||||
if (Array.isArray(selectedDirectory)) {
|
||||
message("Only a single folder can be selected at a time");
|
||||
} else {
|
||||
this.selectedDirectory = selectedDirectory;
|
||||
}
|
||||
if (this.selectedDirectory) {
|
||||
const entries = await readDir(this.selectedDirectory, {
|
||||
recursive: true,
|
||||
});
|
||||
this.files.set(entries.map((entry) => this.mapEntry(entry)));
|
||||
}
|
||||
}
|
||||
|
||||
private mapEntry(entry: FileEntry): FileOrFolder {
|
||||
return {
|
||||
isDirectory: entry.children != null,
|
||||
name: entry.name || "",
|
||||
children: entry.children?.map((entry) => this.mapEntry(entry)),
|
||||
};
|
||||
}
|
||||
}
|
||||
8
src/app/app.config.ts
Normal file
8
src/app/app.config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { ApplicationConfig } from "@angular/core";
|
||||
import { provideRouter } from "@angular/router";
|
||||
import { provideAnimationsAsync } from "@angular/platform-browser/animations/async";
|
||||
import { routes } from "./app.routes";
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(routes), provideAnimationsAsync()],
|
||||
};
|
||||
3
src/app/app.routes.ts
Normal file
3
src/app/app.routes.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Routes } from "@angular/router";
|
||||
|
||||
export const routes: Routes = [];
|
||||
3
src/app/editor/editor.component.css
Normal file
3
src/app/editor/editor.component.css
Normal file
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
1
src/app/editor/editor.component.html
Normal file
1
src/app/editor/editor.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<textarea [(ngModel)]="fileContents" disabled></textarea>
|
||||
31
src/app/editor/editor.component.ts
Normal file
31
src/app/editor/editor.component.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { ChangeDetectionStrategy, Component, input } from "@angular/core";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { readTextFile } from "@tauri-apps/api/fs";
|
||||
import { load, parse } from "protobufjs";
|
||||
|
||||
@Component({
|
||||
selector: "app-editor",
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: "./editor.component.html",
|
||||
styleUrl: "./editor.component.css",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class EditorComponent {
|
||||
fileContents = input<string>();
|
||||
selectedProtoPath = input<string>();
|
||||
|
||||
async loadProtoDefinition() {
|
||||
try {
|
||||
const protoPath = this.selectedProtoPath();
|
||||
if (protoPath) {
|
||||
const protoContents = await readTextFile(protoPath);
|
||||
const definition = await parse(protoContents);
|
||||
console.log(definition);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/app/file-tree/file-tree.component.css
Normal file
3
src/app/file-tree/file-tree.component.css
Normal file
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
95
src/app/file-tree/file-tree.component.ts
Normal file
95
src/app/file-tree/file-tree.component.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { DataSource } from "@angular/cdk/collections";
|
||||
import { FlatTreeControl } from "@angular/cdk/tree";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Signal,
|
||||
computed,
|
||||
input,
|
||||
output,
|
||||
} from "@angular/core";
|
||||
import {
|
||||
MatTreeFlatDataSource,
|
||||
MatTreeFlattener,
|
||||
MatTreeModule,
|
||||
} from "@angular/material/tree";
|
||||
import { MatIconModule } from "@angular/material/icon";
|
||||
|
||||
export interface FileOrFolder {
|
||||
isDirectory: boolean;
|
||||
name: string;
|
||||
children?: FileOrFolder[];
|
||||
}
|
||||
|
||||
interface FileNode {
|
||||
file: FileOrFolder;
|
||||
expandable: boolean;
|
||||
level: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-file-tree",
|
||||
standalone: true,
|
||||
imports: [CommonModule, MatTreeModule, MatIconModule],
|
||||
template: `<mat-tree [dataSource]="dataSource()" [treeControl]="treeControl">
|
||||
<mat-tree-node
|
||||
*matTreeNodeDef="let node"
|
||||
matTreeNodePadding
|
||||
(click)="fileSelected.emit(node.file)"
|
||||
>
|
||||
<button mat-icon-button disabled></button>
|
||||
{{ node.file.name }}
|
||||
</mat-tree-node>
|
||||
<mat-tree-node *matTreeNodeDef="let node; when: hasChild">
|
||||
<button
|
||||
mat-icon-button
|
||||
matTreeNodePadding
|
||||
[attr.aria-label]="'Toggle ' + node.file.name"
|
||||
>
|
||||
<mat-icon class="mat-icon-rtl-mirror">
|
||||
@if (treeControl.isExpanded(node)) { expand_more } @else {
|
||||
chevron_right }
|
||||
</mat-icon>
|
||||
</button>
|
||||
{{ node.name }}
|
||||
</mat-tree-node>
|
||||
</mat-tree>`,
|
||||
styleUrl: "./file-tree.component.css",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FileTreeComponent {
|
||||
files = input<FileOrFolder[]>([]);
|
||||
|
||||
fileSelected = output<FileOrFolder>();
|
||||
|
||||
protected hasChild = (_: number, node: FileNode) => node.expandable;
|
||||
|
||||
private _transformer = (node: FileOrFolder, level: number) => ({
|
||||
expandable: !!node.children && node.children.length > 0,
|
||||
file: node,
|
||||
level: level,
|
||||
});
|
||||
|
||||
protected treeControl = new FlatTreeControl<FileNode>(
|
||||
(node) => node.level,
|
||||
(node) => node.expandable
|
||||
);
|
||||
|
||||
private treeFlattener = new MatTreeFlattener(
|
||||
this._transformer,
|
||||
(node) => node.level,
|
||||
(node) => node.expandable,
|
||||
(node) => node.children
|
||||
);
|
||||
|
||||
// Use computed signals to create datasource
|
||||
protected dataSource = computed(() => {
|
||||
const dataSource = new MatTreeFlatDataSource(
|
||||
this.treeControl,
|
||||
this.treeFlattener
|
||||
);
|
||||
dataSource.data = this.files();
|
||||
return dataSource;
|
||||
});
|
||||
}
|
||||
11
src/app/message.model.ts
Normal file
11
src/app/message.model.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Field as FieldDescriptor } from "protobufjs";
|
||||
|
||||
interface Field<T> {
|
||||
fieldDescriptor: FieldDescriptor;
|
||||
value?: T;
|
||||
}
|
||||
|
||||
interface Message {
|
||||
// messageDescriptor:
|
||||
fields: Field<any>[];
|
||||
}
|
||||
17
src/index.html
Normal file
17
src/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Tauri + Angular</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
7
src/main.ts
Normal file
7
src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { bootstrapApplication } from "@angular/platform-browser";
|
||||
import { appConfig } from "./app/app.config";
|
||||
import { AppComponent } from "./app/app.component";
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
Reference in New Issue
Block a user