retake-dev 0.4.0 → 0.5.1
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/CHANGELOG.md +125 -0
- package/README.md +173 -10
- package/bin/retake.js +14 -1
- package/package.json +7 -1
- package/src/core.js +23 -1
- package/src/mark.svg +1 -0
- package/src/next-dev.js +183 -0
- package/src/next.js +81 -0
- package/src/note-text.js +844 -0
- package/src/plugin.js +7 -3
- package/src/runtime/10-animations.js +9 -0
- package/src/runtime/60-preview.js +3 -1
- package/src/runtime/62-motion.js +106 -0
- package/src/runtime/65-timeline.js +39 -0
- package/src/runtime/67-csssource.js +73 -1
- package/src/runtime/70-boot.js +28 -2
- package/src/server/api.js +73 -1
- package/src/server/front.js +3 -1
- package/src/server/mcp.js +225 -19
- package/src/server/moments.js +317 -0
- package/src/server/sourcemap.js +192 -0
- package/src/shell/00-state.js +7 -0
- package/src/shell/10-dock.js +208 -13
- package/src/shell/15-session.js +66 -10
- package/src/shell/20-timeline.js +47 -16
- package/src/shell/25-input.js +29 -2
- package/src/shell/30-notes.js +709 -113
- package/src/shell/32-anims.js +1353 -0
- package/src/shell/shell.css +79 -8
- package/src/shell/shell.html +10 -3
- package/types/client.d.ts +16 -0
- package/types/index.d.ts +16 -0
- package/types/next.d.ts +50 -0
package/src/shell/shell.css
CHANGED
|
@@ -54,11 +54,40 @@ body.dragging #wb-stage, body.scrubbing #wb-stage { pointer-events: none; }
|
|
|
54
54
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 36px), var(--glass);
|
|
55
55
|
-webkit-backdrop-filter: blur(28px) saturate(160%); backdrop-filter: blur(28px) saturate(160%);
|
|
56
56
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10), 0 -8px 32px rgba(0, 0, 0, 0.35); }
|
|
57
|
+
/* Folded away (the divider dragged to the bottom edge, or ⌥T): it slides down
|
|
58
|
+
out of sight and the round button in the corner brings it back. */
|
|
59
|
+
#wb-dock { padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
60
|
+
transition: transform .24s var(--ease), opacity .2s var(--ease), visibility 0s linear 0s; }
|
|
61
|
+
#wb-dock.will-collapse { opacity: .55; }
|
|
62
|
+
/* Mid-drag it follows the pointer exactly (translateY below the minimum height,
|
|
63
|
+
see 10-dock.js); let go and it eases from there into the icon or back up. */
|
|
64
|
+
body.dragging #wb-dock { transition: opacity .12s var(--ease); will-change: transform; }
|
|
65
|
+
body.collapsed #wb-dock { transform: translateY(calc(100% + 24px)); opacity: 0; visibility: hidden; pointer-events: none;
|
|
66
|
+
transition: transform .24s var(--ease), opacity .2s var(--ease), visibility 0s linear .24s; }
|
|
67
|
+
/* Placed by 10-dock.js (left/top: dragged anywhere, snapped to a side edge). */
|
|
68
|
+
#wb-fab { position: fixed; z-index: 10; left: calc(100vw - 60px); top: calc(100vh - 60px); touch-action: none; cursor: grab;
|
|
69
|
+
width: 44px; height: 44px; border-radius: 999px; color: var(--blue);
|
|
70
|
+
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0)), var(--glass);
|
|
71
|
+
-webkit-backdrop-filter: blur(28px) saturate(160%); backdrop-filter: blur(28px) saturate(160%);
|
|
72
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10), inset 0 0 0 1px var(--hair), var(--shadow); animation: fab-in .22s var(--ease); }
|
|
73
|
+
#wb-fab[hidden] { display: none; }
|
|
74
|
+
#wb-fab.moving { cursor: grabbing; transform: scale(1.06); transition: transform .12s var(--ease); }
|
|
75
|
+
#wb-fab.snapping { transition: left .28s var(--ease), top .28s var(--ease), transform .12s var(--ease); }
|
|
76
|
+
@media (hover: hover) { #wb-fab:hover { background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.02)), var(--glass); } }
|
|
77
|
+
#wb-fab svg { display: block; }
|
|
78
|
+
/* The phase, as the readout shows it: green live, blue playing, amber paused. */
|
|
79
|
+
#wb-fab .dot { position: absolute; top: 9px; right: 9px; width: 6px; height: 6px; border-radius: 999px; background: var(--dim); box-shadow: 0 0 0 2px rgba(22, 22, 26, 0.9); }
|
|
80
|
+
#wb-fab[data-phase="live"] .dot { background: var(--green); }
|
|
81
|
+
#wb-fab[data-phase="playing"] .dot { background: var(--blue); }
|
|
82
|
+
#wb-fab[data-phase="paused"] .dot, #wb-fab[data-phase="scrubbing"] .dot { background: var(--amber); }
|
|
83
|
+
@keyframes fab-in { from { opacity: 0; transform: scale(.6) translateY(8px); } }
|
|
57
84
|
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
85
|
+
#wb-fab { background: rgba(20, 20, 24, 0.92); }
|
|
58
86
|
#wb-dock { background: linear-gradient(to bottom, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 36px), rgba(20, 20, 24, 0.92); }
|
|
59
87
|
}
|
|
60
88
|
/* The grab handle at the top centre: always there, brighter on hover. */
|
|
61
|
-
|
|
89
|
+
/* touch-action: a finger drag on it is the dock's, never the page's scroll. */
|
|
90
|
+
.divider { position: absolute; left: 0; right: 0; top: 0; height: 12px; cursor: ns-resize; z-index: 3; display: flex; justify-content: center; touch-action: none; }
|
|
62
91
|
.divider span { margin-top: 4px; width: 36px; height: 4px; border-radius: 999px; background: rgba(255, 255, 255, 0.18); transition: background .15s var(--ease), width .15s var(--ease); }
|
|
63
92
|
.divider:hover span, body.dragging .divider span { background: rgba(255, 255, 255, 0.45); width: 44px; }
|
|
64
93
|
|
|
@@ -71,12 +100,11 @@ button:active { transform: scale(0.96); }
|
|
|
71
100
|
.head { position: relative; flex: none; height: 48px; margin-top: 8px; display: flex; align-items: center; gap: 8px; padding: 0 16px; }
|
|
72
101
|
.left, .right { display: flex; align-items: center; gap: 8px; flex: none; }
|
|
73
102
|
.right { margin-left: auto; }
|
|
74
|
-
/* Phone width: the tools scroll sideways rather than run off the edge. */
|
|
75
|
-
@media (max-width: 520px) { .head { padding: 0 10px; gap: 6px; } .readout .phase { display: none; } .right { flex: 0 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; } .right::-webkit-scrollbar { display: none; } }
|
|
76
103
|
/* One button style for the text controls: 32px, full round, a ghost fill. */
|
|
77
104
|
.btn { flex: none; height: 32px; padding: 0 14px; gap: 8px; border-radius: 999px; background: var(--fill); color: var(--muted);
|
|
78
105
|
font: 500 13px/1 var(--sans); box-shadow: inset 0 0 0 1px var(--hair); }
|
|
79
106
|
@media (hover: hover) { .btn:hover { color: var(--white); background: var(--fill-2); } }
|
|
107
|
+
.btn.fresh svg { display: none; }
|
|
80
108
|
|
|
81
109
|
/* Transport: play and the clock in one capsule. */
|
|
82
110
|
.transport { display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 14px 0 4px; border-radius: 999px;
|
|
@@ -118,6 +146,29 @@ button:active { transform: scale(0.96); }
|
|
|
118
146
|
box-shadow: 0 0 0 2px rgba(22, 22, 26, 0.9); transform: scale(0); opacity: 0; transition: transform .2s var(--ease), opacity .2s var(--ease); }
|
|
119
147
|
.notes-count.has .n { transform: none; opacity: 1; }
|
|
120
148
|
|
|
149
|
+
/* Phone width: the phase word goes, the row tightens, and Start fresh becomes
|
|
150
|
+
its icon; anything still too wide scrolls sideways rather than run off the edge. */
|
|
151
|
+
@media (max-width: 520px) { .head { padding: 0 10px; gap: 6px; } .readout .phase { display: none; } .right { gap: 6px; flex: 0 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; } .right::-webkit-scrollbar { display: none; } }
|
|
152
|
+
@media (max-width: 440px) { .btn.fresh { width: 32px; padding: 0; } .btn.fresh span { display: none; } .btn.fresh svg { display: block; } }
|
|
153
|
+
/* Fingers (and phone widths): 40px targets. The handle's strip reaches 16px
|
|
154
|
+
above the dock; lane names get a narrower gutter so the track keeps its
|
|
155
|
+
width; the glass is denser, so the page under it doesn't muddy the dock. */
|
|
156
|
+
@media (pointer: coarse), (max-width: 520px) {
|
|
157
|
+
.divider { top: -16px; height: 32px; }
|
|
158
|
+
.divider span { margin-top: 20px; }
|
|
159
|
+
.head { height: 56px; }
|
|
160
|
+
.transport { height: 44px; gap: 8px; padding: 0 12px 0 2px; }
|
|
161
|
+
.play { width: 40px; height: 40px; }
|
|
162
|
+
.tools { height: 44px; padding: 2px; }
|
|
163
|
+
.tool { width: 40px; height: 40px; }
|
|
164
|
+
.btn { height: 40px; }
|
|
165
|
+
.notes-count { width: 32px; }
|
|
166
|
+
}
|
|
167
|
+
@media (max-width: 440px) { .btn.fresh { width: 40px; } }
|
|
168
|
+
@media (max-width: 520px) {
|
|
169
|
+
:root { --glass: rgba(20, 20, 24, 0.9); }
|
|
170
|
+
.gutter { width: 76px; }
|
|
171
|
+
}
|
|
121
172
|
/* The one hint: a small pill. */
|
|
122
173
|
/* Warnings only, centred on the dock. */
|
|
123
174
|
.hint { position: absolute; left: 50%; top: 12px; transform: translateX(-50%); height: 24px; padding: 0 12px; display: inline-flex; align-items: center; border-radius: 999px;
|
|
@@ -163,10 +214,6 @@ body.scrubbing, body.scrubbing .track { cursor: ew-resize; }
|
|
|
163
214
|
body.branching .track, body.branching .track * { cursor: crosshair !important; }
|
|
164
215
|
|
|
165
216
|
/* Following was stopped by hand: this brings it back. */
|
|
166
|
-
.live-btn { position: absolute; right: 8px; top: 24px; z-index: 2; height: 24px; padding: 0 10px; border-radius: 999px;
|
|
167
|
-
background: var(--fill-2); color: var(--white); font: 500 12px/1 var(--sans); }
|
|
168
|
-
@media (hover: hover) { .live-btn:hover { background: rgba(255, 255, 255, 0.18); } }
|
|
169
|
-
.live-btn[hidden] { display: none; }
|
|
170
217
|
|
|
171
218
|
/* ruler: numbers are monospace */
|
|
172
219
|
.lines .rule { stroke: var(--hair); stroke-width: 1; }
|
|
@@ -230,6 +277,7 @@ body.branching .track, body.branching .track * { cursor: crosshair !important; }
|
|
|
230
277
|
#wb-layers .layer.pseudo { padding-left: 14px; }
|
|
231
278
|
#wb-layers .layer.on { color: var(--ink); background: var(--amber); }
|
|
232
279
|
#wb-layers .more { padding: 2px 6px; color: var(--dim); }
|
|
280
|
+
#wb-layers .layer.skipped:not(.on) { color: var(--dim); }
|
|
233
281
|
#wb-hl.select::after { background: var(--white); }
|
|
234
282
|
#wb-scope { position: fixed; pointer-events: none; border: 1px dashed var(--muted); border-radius: 8px; z-index: 14; display: none; }
|
|
235
283
|
#wb-pins { position: fixed; inset: 0; pointer-events: none; z-index: 15; }
|
|
@@ -237,6 +285,7 @@ body.branching .track, body.branching .track * { cursor: crosshair !important; }
|
|
|
237
285
|
color: var(--ink); font: 600 11px/1 var(--mono); box-shadow: 0 0 0 2px rgba(22, 22, 26, 0.85), var(--shadow); transition: transform .12s var(--ease), opacity .15s var(--ease); }
|
|
238
286
|
@media (hover: hover) { .canvas-pin:hover { transform: scale(1.12); } }
|
|
239
287
|
.canvas-pin.away { opacity: .45; }
|
|
288
|
+
body.picking-notes .canvas-pin { pointer-events: none; }
|
|
240
289
|
@media (hover: hover) { .canvas-pin.away:hover { opacity: 1; } }
|
|
241
290
|
|
|
242
291
|
/* ---- popovers: a note, a context menu ---- */
|
|
@@ -292,4 +341,26 @@ body.branching .track, body.branching .track * { cursor: crosshair !important; }
|
|
|
292
341
|
.ctx button.danger { color: var(--red); }
|
|
293
342
|
.ctx .menu-note { padding: 8px; color: var(--dim); font-size: 12px; line-height: 16px; }
|
|
294
343
|
|
|
295
|
-
@media (prefers-reduced-motion: reduce) { * { animation-duration: 0s !important; transition-duration: 0s !important; } }
|
|
344
|
+
@media (prefers-reduced-motion: reduce) { * { animation-duration: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; } }
|
|
345
|
+
|
|
346
|
+
/* The focused element's own row (32-anims.js): its name in the gutter, where it is on its clock in the hint. */
|
|
347
|
+
.focus-name { position: absolute; left: 0; right: 0; padding: 0 8px; display: flex; align-items: center; font: 500 10px/1 var(--mono); color: #fde68a;
|
|
348
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
349
|
+
.gutter.dots .focus-name { padding: 0 2px; font-size: 0; }
|
|
350
|
+
.gutter.dots .focus-name::before { content: ""; width: 8px; height: 8px; margin: 0 auto; border-radius: 2px; background: #fde68a; transform: rotate(45deg); }
|
|
351
|
+
.hint.readout { max-width: max(0px, calc(100% - 420px)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--mono); font-size: 11px; display: block; line-height: 24px; }
|
|
352
|
+
@media (max-width: 760px) { .hint.readout { display: none; } }
|
|
353
|
+
.note-at { margin-top: 6px; font: 500 11px/15px var(--mono); color: #fde68a; }
|
|
354
|
+
.note-asked { all: unset; cursor: pointer; display: block; margin-top: 6px; padding: 3px 8px; border-radius: 6px; font: 400 11px/15px var(--sans); color: var(--muted);
|
|
355
|
+
background: rgba(253, 230, 138, 0.08); border: 1px solid rgba(253, 230, 138, 0.2); }
|
|
356
|
+
.note-asked[hidden] { display: none; }
|
|
357
|
+
@media (hover: hover) { .note-asked:hover { color: var(--fg); } }
|
|
358
|
+
/* Where the open clip moves the element, on the app: its start and end boxes, the path of its centre, the range in full colour. */
|
|
359
|
+
#wb-path { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 14; overflow: visible; }
|
|
360
|
+
#wb-path[hidden] { display: none; }
|
|
361
|
+
#wb-path .edge { fill: none; stroke: #fde68a; stroke-opacity: .55; stroke-width: 1; stroke-dasharray: 4 3; }
|
|
362
|
+
#wb-path .edge.on { stroke-opacity: 1; }
|
|
363
|
+
#wb-path .path { fill: none; stroke: #fde68a; stroke-opacity: .6; stroke-width: 1.5; stroke-dasharray: 1 3; stroke-linecap: round; }
|
|
364
|
+
#wb-path .path.on { stroke-opacity: 1; stroke-dasharray: none; stroke-width: 2; }
|
|
365
|
+
#wb-path .tick { fill: #fff; }
|
|
366
|
+
#wb-path text { font: 600 10px var(--mono); fill: #fde68a; }
|
package/src/shell/shell.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<title>Prototype</title>
|
|
7
7
|
<style>/*CSS*/</style>
|
|
8
8
|
</head>
|
|
@@ -35,21 +35,28 @@
|
|
|
35
35
|
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2z"/><path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/></svg>
|
|
36
36
|
<span class="n">0</span>
|
|
37
37
|
</span>
|
|
38
|
-
<button class="btn fresh" data-a="fresh"
|
|
38
|
+
<button class="btn fresh" data-a="fresh" aria-label="Start fresh" title="Start fresh">
|
|
39
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
|
|
40
|
+
<span>Start fresh</span>
|
|
41
|
+
</button>
|
|
39
42
|
</div>
|
|
40
43
|
</div>
|
|
41
44
|
<div class="body">
|
|
42
45
|
<div class="gutter" aria-label="Timelines"></div>
|
|
43
46
|
<div class="track">
|
|
44
47
|
<canvas class="lines" aria-label="Timeline"></canvas>
|
|
45
|
-
<button class="live-btn" data-a="live" hidden>Live →</button>
|
|
46
48
|
</div>
|
|
47
49
|
</div>
|
|
48
50
|
</div>
|
|
51
|
+
<button id="wb-fab" data-a="expand" aria-label="Show timeline" title="Show timeline (⌥T)" hidden>
|
|
52
|
+
<!--MARK-->
|
|
53
|
+
<i class="dot" aria-hidden="true"></i>
|
|
54
|
+
</button>
|
|
49
55
|
<div id="wb-tip" class="tip" hidden></div>
|
|
50
56
|
<div id="wb-layers" hidden></div>
|
|
51
57
|
<div id="wb-hl"></div>
|
|
52
58
|
<div id="wb-scope"></div>
|
|
59
|
+
<svg id="wb-path" hidden aria-hidden="true"></svg>
|
|
53
60
|
<div id="wb-pins"></div>
|
|
54
61
|
<div id="wb-note" class="card" hidden></div>
|
|
55
62
|
<div id="wb-menu" class="card ctx" hidden></div>
|
package/types/client.d.ts
CHANGED
|
@@ -25,6 +25,13 @@ export interface RetakeClip {
|
|
|
25
25
|
property?: string
|
|
26
26
|
path: Array<string | number>
|
|
27
27
|
iterations?: "infinite" | number
|
|
28
|
+
/** One iteration's duration (ms), for clips that repeat. */
|
|
29
|
+
dur?: number
|
|
30
|
+
/** Start delay (ms), included in `start`..`end`. */
|
|
31
|
+
delay?: number
|
|
32
|
+
/** First and last keyframe's values (up to 4 properties), e.g. `{ opacity: "0" }` → `{ opacity: "1" }`. */
|
|
33
|
+
from?: Record<string, string>
|
|
34
|
+
to?: Record<string, string>
|
|
28
35
|
pseudoElement?: string
|
|
29
36
|
}
|
|
30
37
|
|
|
@@ -69,5 +76,14 @@ export interface RetakeRuntime {
|
|
|
69
76
|
declare global {
|
|
70
77
|
interface Window {
|
|
71
78
|
__retake?: RetakeRuntime | { readonly inert: true }
|
|
79
|
+
/**
|
|
80
|
+
* In the dock's frame: how many px the dock covers at the bottom of the
|
|
81
|
+
* page (0 when it's folded away). A `retake:dock` event on `window`
|
|
82
|
+
* (`detail: { height }`) says when it changes.
|
|
83
|
+
*/
|
|
84
|
+
__retakeDockHeight?: number
|
|
85
|
+
}
|
|
86
|
+
interface WindowEventMap {
|
|
87
|
+
"retake:dock": CustomEvent<{ height: number }>
|
|
72
88
|
}
|
|
73
89
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -78,6 +78,15 @@ export interface ShellOptions {
|
|
|
78
78
|
marker?: "url" | "header"
|
|
79
79
|
/** Rebuilds ask the server for the page as it was recorded (front server only). */
|
|
80
80
|
docs?: boolean
|
|
81
|
+
/**
|
|
82
|
+
* `false`: there's no Retake server behind the dock (a static or serverless
|
|
83
|
+
* deploy). It never requests `/__retake/` (no session, no events) and keeps
|
|
84
|
+
* the session in memory for the page. Default: a server is assumed, and a 404
|
|
85
|
+
* from it falls back to memory.
|
|
86
|
+
*/
|
|
87
|
+
server?: boolean
|
|
88
|
+
/** The project folder: source paths in notes are written relative to it. */
|
|
89
|
+
root?: string
|
|
81
90
|
/** The token mutating `/__retake/` requests carry. */
|
|
82
91
|
token?: string
|
|
83
92
|
}
|
|
@@ -85,6 +94,13 @@ export interface ShellOptions {
|
|
|
85
94
|
/** The dock page: a complete HTML document with the app in a frame. */
|
|
86
95
|
export function shellHtml(options?: ShellOptions): string
|
|
87
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Retake's mark as an SVG string (24×24 viewBox, no width or height): a
|
|
99
|
+
* playhead on a track with an arc back to it. The arc and the played part are
|
|
100
|
+
* `currentColor`, the rest white, so it reads on a dark background.
|
|
101
|
+
*/
|
|
102
|
+
export function markSvg(): string
|
|
103
|
+
|
|
88
104
|
export interface RuntimeTagOptions {
|
|
89
105
|
/** The runtime's setup. */
|
|
90
106
|
rt?: RuntimeConfig
|
package/types/next.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Types for `retake-dev/next`: Retake from a Next.js app's middleware
|
|
2
|
+
// (src/next.js). Dev only: outside `next dev` every request goes straight on.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A middleware (Next 16's `proxy.ts`, Next 15's `middleware.ts`): in `next dev`
|
|
6
|
+
* a top-level page load gets the dock, the dock's frame gets the page with the
|
|
7
|
+
* time runtime first in `<head>`, and `/__retake/*` is the session API (kept
|
|
8
|
+
* in `.retake/`, read by `retake mcp`). Everything else goes on as it was.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* // proxy.ts (Next 16)
|
|
12
|
+
* export { default } from "retake-dev/next"
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* // middleware.ts (Next 15: the Node.js runtime)
|
|
17
|
+
* import retake from "retake-dev/next"
|
|
18
|
+
* export default retake
|
|
19
|
+
* export const config = { runtime: "nodejs" }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Next reads `config` from your file as written (it can't be re-exported).
|
|
23
|
+
* With a matcher of your own, add these entries to it:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* "/__retake/:path*",
|
|
27
|
+
* { source: "/((?!_next/static|_next/image).*)", has: [{ type: "header", key: "sec-fetch-mode", value: "navigate" }] },
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare const retake: (req: Request, event?: unknown) => Promise<Response | undefined>
|
|
31
|
+
export default retake
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Wraps a middleware of your own: in `next dev` Retake answers page loads and
|
|
35
|
+
* `/__retake/*` first; every other request (and every request outside
|
|
36
|
+
* `next dev`) goes to yours. The dock's frame is fetched through your
|
|
37
|
+
* middleware too, so its redirects, rewrites and headers apply to the page.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { withRetake } from "retake-dev/next"
|
|
41
|
+
* export default withRetake(async (req) => { ... })
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function withRetake<M extends (req: any, event?: any) => any>(middleware?: M): (req: Request, event?: unknown) => Promise<Awaited<ReturnType<M>> | Response | undefined>
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Retake's answer to a request, or `undefined` to let it through (always
|
|
48
|
+
* `undefined` outside `next dev`). For a middleware that decides itself.
|
|
49
|
+
*/
|
|
50
|
+
export function retakeResponse(req: Request): Promise<Response | undefined>
|