@toolpath/app-support 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Toolpath
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Toolpath App Support
2
+
3
+ `@toolpath/app-support` is the logic a Toolpath application reuses: the
4
+ preferences, contexts and route helpers every application was otherwise going
5
+ to write for itself, and did.
6
+
7
+ **It renders nothing.** `@toolpath/ui` is the component kit — styling and
8
+ display, the surface documented in
9
+ [Storybook](https://storybook.staging.toolpath.com). This package is the other
10
+ half. Neither depends on the other, and that is the point: a preference read by
11
+ a loader on a server has no business pulling in a Tailwind component kit, and a
12
+ button has no business knowing where a preference is stored.
13
+
14
+ ```
15
+ @toolpath/tool-support depends on nothing
16
+
17
+ @toolpath/app-support the logic ─┐
18
+ ├─ your application
19
+ @toolpath/ui the components ─┘
20
+ ```
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ npm install @toolpath/app-support react react-dom
26
+ ```
27
+
28
+ `react` and `react-dom` are peer dependencies, needed only for the `/react`
29
+ subpath.
30
+
31
+ ## Two entry points
32
+
33
+ `@toolpath/app-support` imports no React. A preference is read by a loader on a
34
+ server as often as by a component in a browser, so the half that does not need
35
+ React does not import it.
36
+
37
+ ```ts
38
+ import { loadUnit, saveUnit } from '@toolpath/app-support'
39
+
40
+ const unit = loadUnit(globalThis.localStorage ?? null, 'catalog.unit')
41
+ ```
42
+
43
+ `@toolpath/app-support/react` is the hooks and contexts.
44
+
45
+ ```tsx
46
+ import { useUnit } from '@toolpath/app-support/react'
47
+
48
+ export const UnitToggle = () => {
49
+ const [unit, choose] = useUnit('catalog.unit')
50
+
51
+ return (
52
+ <button type="button" onClick={() => choose(unit === 'inches' ? 'millimeters' : 'inches')}>
53
+ {unit}
54
+ </button>
55
+ )
56
+ }
57
+ ```
58
+
59
+ ## The unit a person reads in
60
+
61
+ The unit belongs to the person rather than to the thing being looked at: a
62
+ machinist works in one of them all day and should not set it again after
63
+ opening a report or a catalog.
64
+
65
+ - **The storage and the key are the caller's.** Passing the storage in is what
66
+ lets this be called on a server or in a test without a `window`. Taking the
67
+ key means two applications on one origin hold their own units instead of
68
+ silently sharing one.
69
+ - **`loadUnit` reads the older `'in'` spelling as well as `'inches'`.** Those
70
+ values are in people's browsers now, and a reader that accepts only the
71
+ current spelling moves every inch shop to metric the day it deploys.
72
+ - **`useUnit` opens on millimetres and reads the stored preference on the first
73
+ effect**, so a server render and its hydration agree instead of flashing the
74
+ wrong numbers.
75
+
76
+ The vocabulary is `@toolpath/tool-support`'s `UnitSystem`, which is this
77
+ package's one runtime dependency.
78
+
79
+ ## Where this came from
80
+
81
+ `loadUnit`, `saveUnit` and `useUnit` shipped in `@toolpath/ui` 0.2.0 and 0.3.0.
82
+ That was the wrong package: the component kit is styling and display, and
83
+ storage policy is neither. They live here from `@toolpath/ui` 1.0.0 onward.
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,13 @@
1
+ // src/unit-preference.ts
2
+ var loadUnit = (storage, key) => {
3
+ const stored = storage?.getItem(key);
4
+ return stored === "inches" || stored === "in" ? "inches" : "millimeters";
5
+ };
6
+ var saveUnit = (storage, key, unit) => {
7
+ storage?.setItem(key, unit);
8
+ };
9
+
10
+ export {
11
+ loadUnit,
12
+ saveUnit
13
+ };
@@ -0,0 +1,34 @@
1
+ import { UnitSystem } from '@toolpath/tool-support';
2
+
3
+ /**
4
+ * The unit a person reads in, remembered between visits.
5
+ *
6
+ * It belongs to the person rather than to the thing being looked at: a
7
+ * machinist works in one of them all day and should not set it again after
8
+ * opening a report or a catalog.
9
+ *
10
+ * The storage is the caller's, so this can be called in a test or on a server
11
+ * without a `window`, and the key is the caller's too. Two applications on one
12
+ * origin would otherwise silently share the preference — defensible, but a
13
+ * decision each application should make rather than inherit from a constant it
14
+ * cannot see.
15
+ *
16
+ * The vocabulary is `@toolpath/tool-support`'s {@link UnitSystem}, imported
17
+ * rather than restated. Which system a person reads in and which system a
18
+ * vendor published a tool in are the same two values, and a second spelling of
19
+ * them is the lookup table that package exists to have removed.
20
+ */
21
+ /**
22
+ * The stored unit, defaulting to millimetres — what Toolpath states every
23
+ * length in.
24
+ *
25
+ * **`'in'` is read as well as `'inches'`.** The preference was written under
26
+ * the short spelling for as long as that was the vocabulary, and those values
27
+ * are in people's browsers now. A reader that accepts only the current
28
+ * spelling silently moves every inch shop to metric the day it deploys.
29
+ */
30
+ declare const loadUnit: (storage: Pick<Storage, "getItem"> | null, key: string) => UnitSystem;
31
+ /** Writes the current spelling. {@link loadUnit} still reads the old one. */
32
+ declare const saveUnit: (storage: Pick<Storage, "setItem"> | null, key: string, unit: UnitSystem) => void;
33
+
34
+ export { loadUnit, saveUnit };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import {
2
+ loadUnit,
3
+ saveUnit
4
+ } from "./chunk-YPOB3BBR.js";
5
+ export {
6
+ loadUnit,
7
+ saveUnit
8
+ };
@@ -0,0 +1,24 @@
1
+ import { UnitSystem } from '@toolpath/tool-support';
2
+
3
+ /**
4
+ * The unit a person reads in, as a piece of React state.
5
+ *
6
+ * {@link loadUnit} and {@link saveUnit} are the storage; this is the three
7
+ * lines of React that every consumer of them was otherwise going to write. Two
8
+ * applications in the Toolpath template had written it, character for
9
+ * character, differing only in the key — which is the copy this exists to stop
10
+ * being made a third time.
11
+ *
12
+ * **The key is the caller's**, for the reason `unit-preference.ts` gives: two
13
+ * applications on one origin must be able to hold different units, and that is
14
+ * a decision each of them makes rather than inherits.
15
+ *
16
+ * **Read after mount rather than during render.** A server has no
17
+ * `localStorage`, so reading one while rendering makes the first paint depend
18
+ * on a browser that is not there yet — and a value that differed between the
19
+ * two hydrates as a flash of the wrong numbers. The opening state is therefore
20
+ * always millimetres, and the stored preference arrives on the first effect.
21
+ */
22
+ declare const useUnit: (key: string) => [UnitSystem, (next: UnitSystem) => void];
23
+
24
+ export { useUnit };
@@ -0,0 +1,21 @@
1
+ import {
2
+ loadUnit,
3
+ saveUnit
4
+ } from "../chunk-YPOB3BBR.js";
5
+
6
+ // src/react/use-unit.ts
7
+ import { useEffect, useState } from "react";
8
+ var useUnit = (key) => {
9
+ const [unit, setUnit] = useState("millimeters");
10
+ useEffect(() => {
11
+ setUnit(loadUnit(globalThis.localStorage ?? null, key));
12
+ }, [key]);
13
+ const choose = (next) => {
14
+ setUnit(next);
15
+ saveUnit(globalThis.localStorage ?? null, key, next);
16
+ };
17
+ return [unit, choose];
18
+ };
19
+ export {
20
+ useUnit
21
+ };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@toolpath/app-support",
3
+ "version": "0.1.0",
4
+ "description": "Reusable logic for a Toolpath application: preferences, contexts and route helpers",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/toolpath/ui-packages.git",
12
+ "directory": "packages/app-support"
13
+ },
14
+ "homepage": "https://developers.toolpath.com",
15
+ "bugs": {
16
+ "url": "https://github.com/toolpath/ui-packages/issues"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public",
20
+ "registry": "https://registry.npmjs.org"
21
+ },
22
+ "type": "module",
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ },
30
+ "./react": {
31
+ "types": "./dist/react/index.d.ts",
32
+ "import": "./dist/react/index.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "LICENSE",
39
+ "README.md"
40
+ ],
41
+ "sideEffects": false,
42
+ "scripts": {
43
+ "build": "tsup src/index.ts src/react/index.ts --format esm --dts --clean --external react --external react-dom",
44
+ "check-types": "tsc --noEmit",
45
+ "test": "vitest run"
46
+ },
47
+ "dependencies": {
48
+ "@toolpath/tool-support": "workspace:^"
49
+ },
50
+ "peerDependencies": {
51
+ "react": "^19.0.0",
52
+ "react-dom": "^19.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@testing-library/react": "^16.3.2",
56
+ "@types/node": "24.10.1",
57
+ "@types/react": "^19.0.0",
58
+ "@types/react-dom": "^19.0.0",
59
+ "jsdom": "^30.0.1",
60
+ "react": "19.2.0",
61
+ "react-dom": "19.2.0",
62
+ "tsup": "8.5.1",
63
+ "typescript": "5.9.3",
64
+ "vitest": "4.1.10"
65
+ }
66
+ }