@kolkrabbi/kol-shell 0.25.0 → 0.26.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/package.json +4 -4
- package/src/AppShell.jsx +20 -1
- package/src/index.js +1 -0
- package/src/settingsToggle.js +22 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KOL application shell — fixed 48px NavRail + AppShell layout root, PageShell/PageHeader scaffolds, ContentFilters catalog organism, GridCard, SettingsScaffold, WalkthroughPanel, ShortcutsOverlay. App chrome (kol-framework owns site chrome). Nav items, content, shortcuts and settings are consumer-injected. Sits above @kolkrabbi/kol-{theme,component,framework}.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"react-dom": "^18.3.0 || ^19.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@kolkrabbi/kol-component": "^0.
|
|
24
|
-
"@kolkrabbi/kol-icons": "^0.25.0",
|
|
23
|
+
"@kolkrabbi/kol-component": "^0.136.0",
|
|
25
24
|
"@kolkrabbi/kol-framework": "^0.35.0",
|
|
26
|
-
"@kolkrabbi/kol-
|
|
25
|
+
"@kolkrabbi/kol-icons": "^0.25.0",
|
|
26
|
+
"@kolkrabbi/kol-theme": "^0.100.0"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src",
|
package/src/AppShell.jsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
2
|
import NavRail from './NavRail.jsx'
|
|
3
3
|
import { NavHiddenContext } from './navHidden.js'
|
|
4
|
+
import { SettingsToggleContext } from './settingsToggle.js'
|
|
4
5
|
import TouchDeviceOverlay, { useTouchPrimary } from './TouchDeviceOverlay.jsx'
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -71,6 +72,14 @@ import TouchDeviceOverlay, { useTouchPrimary } from './TouchDeviceOverlay.jsx'
|
|
|
71
72
|
* Bare and with ⌥, ignored while typing in a field. Needs
|
|
72
73
|
* `settingsPath`; alone it does nothing.
|
|
73
74
|
*/
|
|
75
|
+
/* the physical-key name for a bound character. Only the keys people actually
|
|
76
|
+
* bind — a full layout table would be a lie about coverage. */
|
|
77
|
+
const CODE_FOR_KEY = {
|
|
78
|
+
',': 'Comma', '.': 'Period', '/': 'Slash', ';': 'Semicolon', "'": 'Quote',
|
|
79
|
+
'[': 'BracketLeft', ']': 'BracketRight', '\\': 'Backslash', '`': 'Backquote',
|
|
80
|
+
'-': 'Minus', '=': 'Equal',
|
|
81
|
+
}
|
|
82
|
+
|
|
74
83
|
export default function AppShell({
|
|
75
84
|
items,
|
|
76
85
|
bottomItems,
|
|
@@ -117,7 +126,15 @@ export default function AppShell({
|
|
|
117
126
|
useEffect(() => {
|
|
118
127
|
if (!settingsKey || !settingsPath) return undefined
|
|
119
128
|
const onKey = (e) => {
|
|
120
|
-
|
|
129
|
+
/* MATCH THE PHYSICAL KEY (SettingsToggleGestureConsumerSeam, kol-fxr
|
|
130
|
+
* 2026-08-30). Option rewrites `e.key` on macOS — **the chord for `,` is
|
|
131
|
+
* `≤`** — so an `e.key` comparison silently drops it while the bare key
|
|
132
|
+
* works, which is the worst way to fail. `e.code` is the same physical key
|
|
133
|
+
* either way; it is why the Option-digit handler above reads `Digit1…`
|
|
134
|
+
* rather than `¡ ™ £`. `e.key` still matches too, so a character with no
|
|
135
|
+
* entry in the table below is unaffected. */
|
|
136
|
+
const wanted = CODE_FOR_KEY[settingsKey]
|
|
137
|
+
if (!(e.key === settingsKey || (wanted && e.code === wanted)) || e.metaKey || e.ctrlKey) return
|
|
121
138
|
const t = e.target
|
|
122
139
|
if (t?.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t?.tagName)) return
|
|
123
140
|
e.preventDefault()
|
|
@@ -191,6 +208,7 @@ export default function AppShell({
|
|
|
191
208
|
|
|
192
209
|
return (
|
|
193
210
|
<NavHiddenContext.Provider value={{ navHidden, setNavHidden }}>
|
|
211
|
+
<SettingsToggleContext.Provider value={toggleSettings}>
|
|
194
212
|
{/* `kol-app-shell` = the app tier: neutral ::selection (kol-theme).
|
|
195
213
|
* A hidden rail zeroes the live width token, so the content's own
|
|
196
214
|
* margin closes with it — one variable, both sides. */}
|
|
@@ -218,6 +236,7 @@ export default function AppShell({
|
|
|
218
236
|
{children}
|
|
219
237
|
</div>
|
|
220
238
|
</div>
|
|
239
|
+
</SettingsToggleContext.Provider>
|
|
221
240
|
</NavHiddenContext.Provider>
|
|
222
241
|
)
|
|
223
242
|
}
|
package/src/index.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export { default as AppShell } from './AppShell.jsx'
|
|
14
14
|
export { NavHiddenContext, useNavHidden } from './navHidden.js'
|
|
15
|
+
export { SettingsToggleContext, useSettingsToggle } from './settingsToggle.js'
|
|
15
16
|
export { default as NavRail } from './NavRail.jsx'
|
|
16
17
|
export { default as PageShell, PageBleed } from './PageShell.jsx'
|
|
17
18
|
export { default as PageHeader } from './PageHeader.jsx'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
/* Settings-toggle context — own file so AppShell exports only components
|
|
4
|
+
* (the react-refresh constraint `navHidden.js` was split out for).
|
|
5
|
+
*
|
|
6
|
+
* WHY A HOOK AND NOT JUST THE PROPS (SettingsToggleGestureConsumerSeam, kol-fxr
|
|
7
|
+
* 2026-08-30). `settingsKey` navigates unconditionally, and that is wrong for an
|
|
8
|
+
* app whose settings are sometimes a DRAWER: on kol-fxr's `/editor`, `,` opens
|
|
9
|
+
* the panel in place and must not leave the canvas. Its rule is "open whatever
|
|
10
|
+
* settings is available", which only the app can know.
|
|
11
|
+
*
|
|
12
|
+
* So the shell keeps what is genuinely shared — the return path, and the rail
|
|
13
|
+
* row toggling — and hands out the toggle for a consumer that owns the gesture.
|
|
14
|
+
* Without this, fxr had to keep its whole local copy (a `lastPage` ref and a
|
|
15
|
+
* branch in `onNavigate`) to keep one line of app-specific routing, which is
|
|
16
|
+
* the duplication `settingsPath` exists to end.
|
|
17
|
+
*
|
|
18
|
+
* No-op when `settingsPath` is unset, and safe outside an AppShell — a hook
|
|
19
|
+
* that throws on a missing provider would make it unusable in exactly the
|
|
20
|
+
* conditional places it is for. */
|
|
21
|
+
export const SettingsToggleContext = createContext(null)
|
|
22
|
+
export const useSettingsToggle = () => useContext(SettingsToggleContext) ?? (() => {})
|