sliftutils 1.2.26 → 1.2.37
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/CLAUDE.md +211 -0
- package/package.json +1 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Project rules
|
|
2
|
+
|
|
3
|
+
These are the binding rules for working in this repo. They were imported
|
|
4
|
+
from `.cursor/rules/*.mdc` so Claude reads them automatically.
|
|
5
|
+
|
|
6
|
+
## General guidelines
|
|
7
|
+
|
|
8
|
+
- The code automatically updates on save, so do not ever run commands to rerun the site.
|
|
9
|
+
- Don't run shell commands when you need to create or move small code files. Use tool calls. Use tool calls to make files within folders — you don't need to make the folder, just make the file, the folder will be created automatically.
|
|
10
|
+
- If you need to add a dependency, don't just edit `package.json`. Use `yarn add` so you get the latest version, unless the user specifies a version.
|
|
11
|
+
- Use tool calls to read files and directories instead of running `ls`, `dir`, etc.
|
|
12
|
+
|
|
13
|
+
## Coding styles
|
|
14
|
+
|
|
15
|
+
- Times should almost always be in milliseconds; assume milliseconds if not told otherwise.
|
|
16
|
+
- Don't make functions that will never be reused and are short. If under 5 lines and not reused, don't create it unless explicitly told to.
|
|
17
|
+
- Comments are used sparingly and only when required to explain what's being done. A comment that just restates the function name is forbidden.
|
|
18
|
+
- Comments go on the line BEFORE the statement, never trailing the semicolon.
|
|
19
|
+
- Use `undefined`, not `null`.
|
|
20
|
+
- Almost never check for `undefined`/`null` specifically — just check truthiness.
|
|
21
|
+
- When a function has more than one primitive parameter that could be confused (e.g. start and end time), put them inside a single object parameter called `config`.
|
|
22
|
+
- Never use return codes — always throw. Include context (expected vs actual). If values could be huge (e.g. file parsing), limit to ~500 characters.
|
|
23
|
+
- Use double quotes, not single quotes.
|
|
24
|
+
- Never use the ternary operator. Convert `x ? y : z` into `x && y || z`.
|
|
25
|
+
- Never use the non-null assertion operator (`!`). Check the value; if needed in nested closures, copy into a `const` to preserve narrowed type.
|
|
26
|
+
- Errors use template strings that include the actual offending value and the expected one: `throw new Error(\`Expected X, was \${y}\`);`
|
|
27
|
+
- Don't use `switch`. Use `if/else`.
|
|
28
|
+
- Don't use `!` to access a value from a `Map`. Use `get` + initialize-if-undefined + `set`.
|
|
29
|
+
- Sort with `import { sort } from "socket-function/src/misc";` — `sort<T>(arr: T[], sortKey: (obj: T) => unknown)`.
|
|
30
|
+
- Prefer early `return` over deep `else`. Handle error cases, warn/throw, then return. The main case should be at the bottom, not nested.
|
|
31
|
+
- Use functions to remove duplication only when something is actually duplicated.
|
|
32
|
+
- Don't recreate collections or URL parameters — import them.
|
|
33
|
+
- Do not redefine types. Import them.
|
|
34
|
+
- Do not annotate types that can be inferred.
|
|
35
|
+
- Constants that might need reconfiguration go near the top of the file under the imports, not buried in functions.
|
|
36
|
+
- Never use environment variables. Configuration goes on disk or via CLI args.
|
|
37
|
+
- Never use inline styles. Always use the `css` helper.
|
|
38
|
+
- Don't use `as any`.
|
|
39
|
+
- When fetch returns `any`, cast it to the real type rather than leaving it as `any`. Same for any deserialized value.
|
|
40
|
+
- DO NOT redeclare constants or types — IMPORT THEM.
|
|
41
|
+
- Don't try/catch for no reason. If you can't handle the exception, let it throw.
|
|
42
|
+
- For input events, always use `event.currentTarget`.
|
|
43
|
+
- Use `ref={elem => …}` callbacks. NEVER use `React.createRef`.
|
|
44
|
+
- NEVER render images with a fixed width AND height. This stretches or crops them. Set only width OR height.
|
|
45
|
+
- Avoid callback hell with `import { PromiseObj } from "socket-function/src/misc";` — wrap event callbacks in a `PromiseObj` and await it.
|
|
46
|
+
- `import { keyBy, keyByArray } from "socket-function/src/misc";` for building lookups.
|
|
47
|
+
- Never use `alert`. Throw instead.
|
|
48
|
+
|
|
49
|
+
## MobX state
|
|
50
|
+
|
|
51
|
+
We use MobX. Components store local state in a field called `synced`, which is an `observable`. Never use `Component.state`. Components need the `@observer` decorator.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import preact from "preact";
|
|
55
|
+
import { observable } from "mobx";
|
|
56
|
+
import { observer } from "sliftutils/render-utils/observer";
|
|
57
|
+
|
|
58
|
+
@observer
|
|
59
|
+
class Example extends preact.Component {
|
|
60
|
+
synced = observable({
|
|
61
|
+
x: 0,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
render() {
|
|
65
|
+
return <div>
|
|
66
|
+
<button onClick={() => this.synced.x++}>
|
|
67
|
+
Click me
|
|
68
|
+
</button>
|
|
69
|
+
<p>
|
|
70
|
+
{this.synced.x}
|
|
71
|
+
</p>
|
|
72
|
+
</div>;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Styling and CSS
|
|
78
|
+
|
|
79
|
+
- Never use `em` or `rem`. Use `px` or `vw`/`vh`/`%`.
|
|
80
|
+
- Don't add font colors / aesthetics / `fontSize` beyond `hbox`/`vbox`/`pad2` unless asked. If you think styling could help, *tell the user* — don't add it unprompted.
|
|
81
|
+
- Never use `h1`/`h2`/`h3` etc. — set the font size explicitly instead.
|
|
82
|
+
- Don't use `fillWidth` where `flexGrow(1)` would do.
|
|
83
|
+
- Add very little styling (colors, rounding, etc.) unless asked.
|
|
84
|
+
|
|
85
|
+
### The `css` helper
|
|
86
|
+
|
|
87
|
+
All styling goes through the `css` helper.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<div className={css.width(100).height(50)}>…</div>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Chains of properties are fine across two lines:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
className={css.size(100, 100).hbox(4)
|
|
97
|
+
.hsl(0, 50, 50).borderRadius(4)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Conditionals come after, never as a ternary:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
className={css
|
|
105
|
+
.size(100, 100).hbox(4)
|
|
106
|
+
+ (isDimmed && css.opacity(0.5))
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Aliases
|
|
111
|
+
|
|
112
|
+
Non-call aliases (chainable): `relative`, `absolute`, `fixed`, `wrap`, `marginAuto`, `fillBoth`, `fillWidth`, `fillHeight`, `flexShrink0`, `ellipsis`, `overflowAuto`, `overflowHidden`.
|
|
113
|
+
|
|
114
|
+
Call aliases: `hbox(gap, rowGap?)`, `vbox(gap, columnGap?)`, `pad2(value, vertical?)`, `hsl/hsla(...)`, `hslhover/hslahover`, `bord/bord2`, `hslcolor/hslacolor`, `size(w, h)`, `pos(x, y)`.
|
|
115
|
+
|
|
116
|
+
Use `css.button` to make a *non-button* feel like a button (hover background + pointer cursor) — only when a background color is set, and never on actual `<button>`/`<Button>`.
|
|
117
|
+
|
|
118
|
+
Prefer `hbox`/`vbox` for spacing between elements over margins.
|
|
119
|
+
|
|
120
|
+
### Animations
|
|
121
|
+
|
|
122
|
+
Keyframes go in a `<style>` tag:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
<style>{`
|
|
126
|
+
@keyframes spinner-ring {
|
|
127
|
+
0% { transform: rotate(0deg); }
|
|
128
|
+
100% { transform: rotate(360deg); }
|
|
129
|
+
}
|
|
130
|
+
`}</style>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Components
|
|
134
|
+
|
|
135
|
+
### Anchor
|
|
136
|
+
|
|
137
|
+
`Anchor` is the `<a>` for navigation tied to `URLParam`:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
<Anchor params={[[todolistURL, listKey]]}>
|
|
141
|
+
{list.name}
|
|
142
|
+
</Anchor>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`URLParam` stores a value in the URL. Second argument is the default (number, string, or object). Use `.value` to get/set.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
const todolistURL = new URLParam("todolist", "");
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### InputLabel
|
|
152
|
+
|
|
153
|
+
Use `InputLabel` / `InputLabelURL` for inputs:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<InputLabelURL
|
|
157
|
+
label="Show Previous Video"
|
|
158
|
+
checkbox
|
|
159
|
+
persisted={showPreviousVideoURL}
|
|
160
|
+
/>
|
|
161
|
+
<InputLabel
|
|
162
|
+
label="Notes"
|
|
163
|
+
fillWidth
|
|
164
|
+
value={node.notes || ""}
|
|
165
|
+
onChangeValue={async (value) => {
|
|
166
|
+
const updatedNode = deepCloneJSON(node);
|
|
167
|
+
updatedNode.notes = value;
|
|
168
|
+
await VideoNode.set(node.id, updatedNode);
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## DiskCollection
|
|
174
|
+
|
|
175
|
+
If you read from a collection, mutate, and want to write back, shallow-copy so the collection notices the change:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
let x = collection.get("x");
|
|
179
|
+
x.y = Math.random();
|
|
180
|
+
collection.set("x", { ...x });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Non-async getters go in render functions; async getters go in event handlers. `.set` works in both.
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
get(key: string): T | undefined;
|
|
187
|
+
async getPromise(key: string): Promise<T | undefined>;
|
|
188
|
+
set(key: string, value: T): void;
|
|
189
|
+
remove(key: string): void;
|
|
190
|
+
getKeys(): string[];
|
|
191
|
+
getKeysPromise(): Promise<string[]>;
|
|
192
|
+
getEntries(): [string, T][];
|
|
193
|
+
getValues(): T[];
|
|
194
|
+
async getValuesPromise(): Promise<T[]>;
|
|
195
|
+
getInfo(key: string);
|
|
196
|
+
async reset();
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## API calls
|
|
200
|
+
|
|
201
|
+
In an event callback (which must be async):
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
APIController(getExtNodeId()).getModels.promise()
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
In a render function:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
APIController(getExtNodeId()).getModels()
|
|
211
|
+
```
|