diff --git a/README.md b/README.md
index ed7dddb..a26befd 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
[![npm](https://img.shields.io/npm/v/nextline-web)](https://www.npmjs.com/package/nextline-web)
[![Unit tests](https://github.com/simonsobs/nextline-web/actions/workflows/unit-test.yml/badge.svg)](https://github.com/simonsobs/nextline-web/actions/workflows/unit-test.yml)
-
The front-end web app of Nextline.
_Nextline_ is a DAQ sequencer of the [Observatory Control System
@@ -19,7 +18,6 @@ unless you need to refer to a specific package.
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.11451619.svg)](https://doi.org/10.5281/zenodo.11451619)
-
## Screenshot
![Screenshot](screenshot.png)
@@ -39,11 +37,15 @@ Docker images of the Nextline front-end web app are created as
You can configure the web app in the container with these variables.
-| Environment variable | Default value | Description |
-| -------------------- | ----------------------- | ------------------------------------- |
-| `PUBLIC_PATH` | `/` | Path in the URL of the web app |
-| `API_HTTP` | `http://localhost:8000` | URL of the GraphQL API server |
-| `API_NAME` | `localhost` | Text to be shown as part of the title |
+| Environment variable | Default value | Description |
+| -------------------- | ----------------------- | ------------------------------------------ |
+| `PUBLIC_PATH` | `/` | Path in the URL of the web app |
+| `API_HTTP` | `http://localhost:8000` | URL of the GraphQL API server |
+| `API_NAME` | `localhost` | Text to be shown as part of the title |
+| `SEED_COLOR` | `#607D8B` | The source color (hex) of dynamic colors\* |
+
+\*Accessible colors in light and dark modes are dynamically generated by
+[Dynamic Color in Material Design](https://m3.material.io/styles/color/).
For example, if you are to run the web app at the port `8080` with the path
`/nextline/` and use the GraphQL API server at `http://localhost:5000/graphql` as the name `API 1`, you can do so with the following command.
diff --git a/docker/config.json b/docker/config.json
index 71c2296..06ac627 100644
--- a/docker/config.json
+++ b/docker/config.json
@@ -1,4 +1,5 @@
{
"apiName": "localhost",
- "apiHttp": "http://localhost:8000"
+ "apiHttp": "http://localhost:8000",
+ "seedColor": "#607D8B"
}
diff --git a/public/config.json b/public/config.json
index 71c2296..06ac627 100644
--- a/public/config.json
+++ b/public/config.json
@@ -1,4 +1,5 @@
{
"apiName": "localhost",
- "apiHttp": "http://localhost:8000"
+ "apiHttp": "http://localhost:8000",
+ "seedColor": "#607D8B"
}
diff --git a/src/app/AppMain.vue b/src/app/AppMain.vue
index 6a1c30f..9f65cc8 100644
--- a/src/app/AppMain.vue
+++ b/src/app/AppMain.vue
@@ -38,9 +38,11 @@ stay hidden.
-->
diff --git a/src/utils/config/config.ts b/src/utils/config/config.ts
index 8ce5469..bc661da 100644
--- a/src/utils/config/config.ts
+++ b/src/utils/config/config.ts
@@ -2,6 +2,7 @@ export interface Config {
appName: string;
apiName: string;
apiHttp: string;
+ seedColor: string;
}
export const defaultConfig = {
@@ -18,4 +19,8 @@ export function validateConfig(config: Config) {
if (typeof config.apiHttp !== "string") throw Error("apiHttp is not string");
if (config?.apiHttp === "") throw Error("apiHttp is empty");
+
+ if (typeof config.seedColor !== "string") throw Error("seedColor is not string");
+ if (!/^#([0-9A-F]{3}|[0-9A-F]{6}|[0-9A-F]{8})$/i.test(config.seedColor))
+ throw Error(`seedColor is not a valid hex color: ${config.seedColor}`);
}
diff --git a/src/utils/config/index.ts b/src/utils/config/index.ts
index c4ee90f..5be54db 100644
--- a/src/utils/config/index.ts
+++ b/src/utils/config/index.ts
@@ -19,8 +19,9 @@ export const useConfig = () => useConfigT();
* Provide the config to the child components.
* In the child components, use `useConfig` to get the config.
*/
-export const useProvideConfig = (config: MaybeRefOrGetter) =>
- useProvideConfigT(config);
+export const useProvideConfig = (
+ config: Exclude, null>
+) => useProvideConfigT(config);
/**
* Read the config from the config file.
diff --git a/src/utils/config/provide-config.ts b/src/utils/config/provide-config.ts
index 7a2b987..f0e6bff 100644
--- a/src/utils/config/provide-config.ts
+++ b/src/utils/config/provide-config.ts
@@ -2,15 +2,36 @@ import { ref, provide, watchEffect, toValue } from "vue";
import type { MaybeRefOrGetter, InjectionKey, Ref } from "vue";
import { injectionKey } from "./key";
-
/**
- * Provide the config of type `T` to the child components.
- * In the child components, use `useConfig` to get the config.
+ * Provides a configuration of type `T` to child components.
+ *
+ * This function creates a reactive reference to the config and provides it
+ * to child components using Vue's provide/inject system.
+ *
+ * @template T The type of the configuration object
+ * @param The configuration, which can be:
+ * - A value of type T
+ * - A Ref
+ * - A getter function returning T or null
+ *
+ * @remarks
+ * In child components, use `useConfig` to retrieve this provided config.
+ * The config will only be provided once it has a non-null value.
+ * Subsequent updates to the config (including to undefined) will be reflected
+ * in the provided reference, but will not trigger a new provide.
*/
-export function useProvideConfigT(config: MaybeRefOrGetter) {
- const configRef = ref(toValue(config)) as Ref;
+export function useProvideConfigT(
+ config: Exclude, null>
+) {
+ const configRef = ref() as Ref;
+ let provided = false;
+
watchEffect(() => {
- configRef.value = toValue(config);
+ const value = toValue(config);
+ if (value === null) return;
+ configRef.value = value;
+ if (provided) return;
+ provide(injectionKey as InjectionKey[>, configRef);
+ provided = true;
});
- provide(injectionKey as InjectionKey][>, configRef);
}
]