Add a basic, empty spline UI based on Angular
The page doesn't do anything yet, but should let people build up a
more complex UI.
This is effectively a super stripped down version of the scouting app.
Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: Ia97a3670439f4c3f208f8110282645aa0e3862f1
diff --git a/.bazelignore b/.bazelignore
index 7fa8e45..cce9d62 100644
--- a/.bazelignore
+++ b/.bazelignore
@@ -1,5 +1,6 @@
external
node_modules
+frc971/control_loops/swerve/spline_ui/www/node_modules
scouting/webserver/requests/messages/node_modules
scouting/www/node_modules
scouting/www/driver_ranking/node_modules
diff --git a/WORKSPACE b/WORKSPACE
index 6be969d..ad59314 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -944,6 +944,7 @@
name = "npm",
data = [
"//aos/analysis/foxglove_extension:package.json",
+ "//control_loops/swerve/spline_ui/www:package.json",
"@//:package.json",
"@//:pnpm-workspace.yaml",
"@//scouting/webserver/requests/messages:package.json",
diff --git a/frc971/control_loops/swerve/spline_ui/www/BUILD b/frc971/control_loops/swerve/spline_ui/www/BUILD
new file mode 100644
index 0000000..1ecd17c
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/BUILD
@@ -0,0 +1,46 @@
+load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file")
+load("@npm//:defs.bzl", "npm_link_all_packages")
+load("//tools/build_rules:js.bzl", "ng_application")
+load("//tools/build_rules/js:static.bzl", "assemble_static_files")
+
+npm_link_all_packages(name = "node_modules")
+
+ng_application(
+ name = "app",
+ assets = [
+ # Explicitly blank to avoid the default.
+ ],
+ extra_srcs = [
+ "app/common.css",
+ ],
+ html_assets = [
+ "favicon.ico",
+ ],
+ deps = [
+ ":node_modules",
+ ],
+)
+
+assemble_static_files(
+ name = "static_files",
+ srcs = [
+ "//third_party/y2024/field:pictures",
+ ],
+ app_files = ":app",
+ replace_prefixes = {
+ "prod": "",
+ "dev": "",
+ "third_party/y2024": "pictures",
+ },
+ tags = [
+ "no-remote-cache",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+copy_file(
+ name = "app_common_css",
+ src = "common.css",
+ out = "app/common.css",
+ visibility = [":__subpackages__"],
+)
diff --git a/frc971/control_loops/swerve/spline_ui/www/README.md b/frc971/control_loops/swerve/spline_ui/www/README.md
new file mode 100644
index 0000000..17960c7
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/README.md
@@ -0,0 +1,10 @@
+Scouting App Frontend
+================================================================================
+
+Run using:
+```console
+$ bazel build //frc971/control_loops/swerve/spline_ui/www:all
+$ (cd bazel-bin/frc971/control_loops/swerve/spline_ui/www/static_files/ && python3 -m http.server)
+```
+
+Then you can navigate to <http://localhost:8000> to look at the web page.
diff --git a/frc971/control_loops/swerve/spline_ui/www/app/app.module.ts b/frc971/control_loops/swerve/spline_ui/www/app/app.module.ts
new file mode 100644
index 0000000..6948041
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/app/app.module.ts
@@ -0,0 +1,16 @@
+import {NgModule} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+
+import {App} from './app';
+
+@NgModule({
+ declarations: [App],
+ imports: [
+ BrowserModule,
+ BrowserAnimationsModule,
+ ],
+ exports: [App],
+ bootstrap: [App],
+})
+export class AppModule {}
diff --git a/frc971/control_loops/swerve/spline_ui/www/app/app.ng.html b/frc971/control_loops/swerve/spline_ui/www/app/app.ng.html
new file mode 100644
index 0000000..6e43eac
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/app/app.ng.html
@@ -0,0 +1 @@
+<h1>Spline UI</h1>
diff --git a/frc971/control_loops/swerve/spline_ui/www/app/app.ts b/frc971/control_loops/swerve/spline_ui/www/app/app.ts
new file mode 100644
index 0000000..dc0cb7c
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/app/app.ts
@@ -0,0 +1,9 @@
+import {Component} from '@angular/core';
+
+@Component({
+ selector: 'spline-ui-app',
+ templateUrl: './app.ng.html',
+ styleUrls: ['../app/common.css'],
+})
+export class App {
+}
diff --git a/frc971/control_loops/swerve/spline_ui/www/common.css b/frc971/control_loops/swerve/spline_ui/www/common.css
new file mode 100644
index 0000000..cf58bba
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/common.css
@@ -0,0 +1 @@
+/* This CSS is shared between all spline UI pages. */
diff --git a/frc971/control_loops/swerve/spline_ui/www/favicon.ico b/frc971/control_loops/swerve/spline_ui/www/favicon.ico
new file mode 100644
index 0000000..2bf69ea
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/favicon.ico
Binary files differ
diff --git a/frc971/control_loops/swerve/spline_ui/www/index.html b/frc971/control_loops/swerve/spline_ui/www/index.html
new file mode 100644
index 0000000..c36936d
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/index.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <title>FRC971 Spline UI</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="theme-color" content="#000000" />
+ <link
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
+ rel="stylesheet"
+ integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
+ crossorigin="anonymous"
+ />
+ <link
+ rel="stylesheet"
+ href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
+ />
+ <script
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
+ integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
+ crossorigin="anonymous"
+ ></script>
+ </head>
+ <body>
+ <spline-ui-app></spline-ui-app>
+ <noscript>
+ Please enable JavaScript to continue using this application.
+ </noscript>
+ </body>
+</html>
diff --git a/frc971/control_loops/swerve/spline_ui/www/main.ts b/frc971/control_loops/swerve/spline_ui/www/main.ts
new file mode 100644
index 0000000..1f8eb00
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/main.ts
@@ -0,0 +1,4 @@
+import {platformBrowser} from '@angular/platform-browser';
+import {AppModule} from './app/app.module';
+
+platformBrowser().bootstrapModule(AppModule);
diff --git a/frc971/control_loops/swerve/spline_ui/www/package.json b/frc971/control_loops/swerve/spline_ui/www/package.json
new file mode 100644
index 0000000..54a7f09
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/package.json
@@ -0,0 +1,6 @@
+{
+ "private": true,
+ "dependencies": {
+ "@angular/animations": "v16-lts"
+ }
+}
diff --git a/frc971/control_loops/swerve/spline_ui/www/polyfills.ts b/frc971/control_loops/swerve/spline_ui/www/polyfills.ts
new file mode 100644
index 0000000..e4555ed
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/polyfills.ts
@@ -0,0 +1,52 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes recent versions of Safari, Chrome (including
+ * Opera), Edge on the desktop, and iOS and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d8164ab..c8e5425 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -170,6 +170,12 @@
specifier: ^5
version: 5.1.6
+ frc971/control_loops/swerve/spline_ui/www:
+ dependencies:
+ '@angular/animations':
+ specifier: v16-lts
+ version: 16.2.12(@angular/core@16.2.12)
+
scouting/webserver/requests/messages: {}
scouting/www:
@@ -390,7 +396,7 @@
'@angular/core': 16.2.12
dependencies:
'@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
- tslib: 2.6.0
+ tslib: 2.6.2
/@angular/cli@16.2.12:
resolution: {integrity: sha512-Pcbiraoqdw4rR2Ey5Ooy0ESLS1Ffbjkb6sPfinKRkHmAvyqsmlvkfbB/qK8GrzDSFSWvAKMMXRw9l8nbjvQEXg==}
@@ -2200,7 +2206,6 @@
function-bind: 1.1.2
get-intrinsic: 1.2.4
set-function-length: 1.2.2
- dev: false
/callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
@@ -2639,7 +2644,6 @@
es-define-property: 1.0.0
es-errors: 1.3.0
gopd: 1.0.1
- dev: false
/define-lazy-prop@2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
@@ -2878,12 +2882,10 @@
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.4
- dev: false
/es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
- dev: false
/es-iterator-helpers@1.0.19:
resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==}
@@ -3567,7 +3569,6 @@
has-proto: 1.0.1
has-symbols: 1.0.3
hasown: 2.0.1
- dev: false
/get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
@@ -3708,7 +3709,6 @@
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
get-intrinsic: 1.2.4
- dev: false
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -3734,7 +3734,6 @@
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
dependencies:
es-define-property: 1.0.0
- dev: false
/has-proto@1.0.1:
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
@@ -4977,7 +4976,6 @@
/object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
- dev: false
/object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
@@ -5385,7 +5383,7 @@
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
dependencies:
- side-channel: 1.0.4
+ side-channel: 1.0.6
/querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
@@ -5652,7 +5650,7 @@
/rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
dependencies:
- tslib: 2.6.0
+ tslib: 2.6.2
dev: true
/safe-array-concat@1.1.2:
@@ -5756,7 +5754,6 @@
get-intrinsic: 1.2.4
gopd: 1.0.1
has-property-descriptors: 1.0.2
- dev: false
/set-function-name@2.0.2:
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
@@ -5799,7 +5796,6 @@
es-errors: 1.3.0
get-intrinsic: 1.2.4
object-inspect: 1.13.1
- dev: false
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -6293,7 +6289,6 @@
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- dev: false
/tsutils@3.21.0(typescript@5.1.6):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index f18844b..6675e2f 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,4 +1,5 @@
packages:
+- frc971/control_loops/swerve/spline_ui/www
- scouting/webserver/requests/messages
- scouting/www
- scouting/www/*
diff --git a/scouting/www/BUILD b/scouting/www/BUILD
index c73f93a..2a34fdf 100644
--- a/scouting/www/BUILD
+++ b/scouting/www/BUILD
@@ -3,7 +3,8 @@
load("@npm//:@angular/service-worker/package_json.bzl", angular_service_worker = "bin")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("//tools/build_rules:js.bzl", "ng_application")
-load(":defs.bzl", "assemble_service_worker_files", "assemble_static_files")
+load("//tools/build_rules/js:static.bzl", "assemble_static_files")
+load(":defs.bzl", "assemble_service_worker_files")
npm_link_all_packages(name = "node_modules")
diff --git a/scouting/www/defs.bzl b/scouting/www/defs.bzl
index 86095d7..a8bc775 100644
--- a/scouting/www/defs.bzl
+++ b/scouting/www/defs.bzl
@@ -1,41 +1,3 @@
-load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory_bin_action")
-
-def _assemble_static_files_impl(ctx):
- out_dir = ctx.actions.declare_directory(ctx.label.name)
-
- copy_to_directory_bin = ctx.toolchains["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"].copy_to_directory_info.bin
-
- copy_to_directory_bin_action(
- ctx,
- dst = out_dir,
- name = ctx.label.name,
- copy_to_directory_bin = copy_to_directory_bin,
- files = ctx.files.srcs + ctx.attr.app_files.files.to_list(),
- replace_prefixes = ctx.attr.replace_prefixes,
- )
-
- return [DefaultInfo(
- files = depset([out_dir]),
- runfiles = ctx.runfiles([out_dir]),
- )]
-
-assemble_static_files = rule(
- implementation = _assemble_static_files_impl,
- attrs = {
- "app_files": attr.label(
- mandatory = True,
- ),
- "srcs": attr.label_list(
- mandatory = True,
- allow_files = True,
- ),
- "replace_prefixes": attr.string_dict(
- mandatory = True,
- ),
- },
- toolchains = ["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"],
-)
-
def _assemble_service_worker_files_impl(ctx):
args = ctx.actions.args()
args.add_all(ctx.attr._package.files, before_each = "--input_dir", expand_directories = False)
diff --git a/scouting/www/test/authorize/BUILD b/scouting/www/test/authorize/BUILD
index af8bc70..e982298 100644
--- a/scouting/www/test/authorize/BUILD
+++ b/scouting/www/test/authorize/BUILD
@@ -1,8 +1,8 @@
load("@aspect_rules_js//js:defs.bzl", "js_test")
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@npm//:defs.bzl", "npm_link_all_packages")
-load("//scouting/www:defs.bzl", "assemble_static_files")
load("//tools/build_rules:js.bzl", "ng_application")
+load("//tools/build_rules/js:static.bzl", "assemble_static_files")
npm_link_all_packages(name = "node_modules")
diff --git a/tools/build_rules/js.bzl b/tools/build_rules/js.bzl
index d7096a9..a95d5f2 100644
--- a/tools/build_rules/js.bzl
+++ b/tools/build_rules/js.bzl
@@ -84,8 +84,8 @@
assets: assets to include in the file bundle
visibility: visibility of the primary targets ({name}, 'test', 'serve')
"""
- assets = assets if assets else native.glob(["assets/**/*"])
- html_assets = html_assets if html_assets else []
+ assets = assets if assets != None else native.glob(["assets/**/*"])
+ html_assets = html_assets or []
test_spec_srcs = native.glob(["app/**/*.spec.ts"])
diff --git a/tools/build_rules/js/static.bzl b/tools/build_rules/js/static.bzl
new file mode 100644
index 0000000..a2fe2ee
--- /dev/null
+++ b/tools/build_rules/js/static.bzl
@@ -0,0 +1,37 @@
+load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory_bin_action")
+
+def _assemble_static_files_impl(ctx):
+ out_dir = ctx.actions.declare_directory(ctx.label.name)
+
+ copy_to_directory_bin = ctx.toolchains["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"].copy_to_directory_info.bin
+
+ copy_to_directory_bin_action(
+ ctx,
+ dst = out_dir,
+ name = ctx.label.name,
+ copy_to_directory_bin = copy_to_directory_bin,
+ files = ctx.files.srcs + ctx.attr.app_files.files.to_list(),
+ replace_prefixes = ctx.attr.replace_prefixes,
+ )
+
+ return [DefaultInfo(
+ files = depset([out_dir]),
+ runfiles = ctx.runfiles([out_dir]),
+ )]
+
+assemble_static_files = rule(
+ implementation = _assemble_static_files_impl,
+ attrs = {
+ "app_files": attr.label(
+ mandatory = True,
+ ),
+ "srcs": attr.label_list(
+ mandatory = True,
+ allow_files = True,
+ ),
+ "replace_prefixes": attr.string_dict(
+ mandatory = True,
+ ),
+ },
+ toolchains = ["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"],
+)