cursedbelt 5.77.0 → 5.78.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/dist/react/canvas-hud/CanvasToolbar.d.ts +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.d.ts.map +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.js +2 -2
- package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -1
- package/dist/react/components/ListToolbar.d.ts +2 -2
- package/dist/react/components/ListToolbar.d.ts.map +1 -1
- package/dist/react/components/ListToolbar.js +1 -1
- package/dist/react/hooks/useOutsidePress.d.ts.map +1 -1
- package/dist/react/hooks/useOutsidePress.js +18 -3
- package/dist/react/hooks/useOutsidePress.js.map +1 -1
- package/dist/react/hooks/useScrollMemory.d.ts +4 -3
- package/dist/react/hooks/useScrollMemory.d.ts.map +1 -1
- package/dist/react/hooks/useScrollMemory.js +16 -2
- package/dist/react/hooks/useScrollMemory.js.map +1 -1
- package/dist/react/media/FocusViewer.d.ts +8 -1
- package/dist/react/media/FocusViewer.d.ts.map +1 -1
- package/dist/react/media/FocusViewer.js +174 -10
- package/dist/react/media/FocusViewer.js.map +1 -1
- package/dist/react/media/focusViewer.css +31 -0
- package/dist/react/media/gifClip.d.ts +46 -0
- package/dist/react/media/gifClip.d.ts.map +1 -0
- package/dist/react/media/gifClip.js +74 -0
- package/dist/react/media/gifClip.js.map +1 -0
- package/dist/react/media/gifEncode.d.ts +46 -0
- package/dist/react/media/gifEncode.d.ts.map +1 -0
- package/dist/react/media/gifEncode.js +288 -0
- package/dist/react/media/gifEncode.js.map +1 -0
- package/dist/react/media/gifRecord.d.ts +26 -0
- package/dist/react/media/gifRecord.d.ts.map +1 -0
- package/dist/react/media/gifRecord.js +93 -0
- package/dist/react/media/gifRecord.js.map +1 -0
- package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
- package/dist/styles-areas/analytics.css +9 -9
- package/dist/styles-areas/data-table.css +10 -10
- package/dist/styles-areas/media.css +2 -2
- package/package.json +3 -3
- package/scripts/guardrailsEnforce.spec.ts +10 -0
- package/scripts/guardrailsEnforce.ts +4 -1
- package/scripts/styleAreas.ts +3 -1
- package/src/react/canvas-hud/CanvasToolbar.tsx +1 -1
- package/src/react/components/DataTable/TableFilterBuilder.tsx +9 -13
- package/src/react/components/DataTable/tableSystem.spec.tsx +15 -7
- package/src/react/components/ListToolbar.tsx +2 -2
- package/src/react/hooks/useOutsidePress.spec.tsx +20 -0
- package/src/react/hooks/useOutsidePress.ts +17 -2
- package/src/react/hooks/useScrollMemory.spec.tsx +45 -2
- package/src/react/hooks/useScrollMemory.ts +20 -5
- package/src/react/media/FocusViewer.tsx +322 -25
- package/src/react/media/focusViewer.css +31 -0
- package/src/react/media/gifClip.ts +90 -0
- package/src/react/media/gifEncode.spec.ts +213 -0
- package/src/react/media/gifEncode.ts +298 -0
- package/src/react/media/gifRecord.ts +103 -0
- package/src/styles-areas/analytics.css +9 -9
- package/src/styles-areas/data-table.css +10 -10
- package/src/styles-areas/media.css +2 -2
|
@@ -3,7 +3,7 @@ export interface CanvasToolbarProps {
|
|
|
3
3
|
/** The left half: what the canvas shows (a reach, a layer switch). Hidden while the bar is
|
|
4
4
|
* under 768px wide when `compactFilters` is given — five stops beside the tools would scroll out of a thumb's sight. */
|
|
5
5
|
filters?: ReactNode;
|
|
6
|
-
/** The same choice as a phone wants it, usually
|
|
6
|
+
/** The same choice as a phone wants it, usually one `SelectField`: shown only while
|
|
7
7
|
* the bar is under 768px wide, on row two beside the legend. */
|
|
8
8
|
compactFilters?: ReactNode;
|
|
9
9
|
/** A key or chip strip (houses, layers, tags): ONE line that scrolls inside itself, its ends
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableFilterBuilder.d.ts","sourceRoot":"","sources":["../../../../src/react/components/DataTable/TableFilterBuilder.tsx"],"names":[],"mappings":"AAOA,OAAO,EAOL,KAAK,WAAW,EAIjB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,KAAK,EACL,QAAQ,EACR,SAAS,EACT,WAAmB,GACpB,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"TableFilterBuilder.d.ts","sourceRoot":"","sources":["../../../../src/react/components/DataTable/TableFilterBuilder.tsx"],"names":[],"mappings":"AAOA,OAAO,EAOL,KAAK,WAAW,EAIjB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,KAAK,EACL,QAAQ,EACR,SAAS,EACT,WAAmB,GACpB,EAAE,uBAAuB,sCA+LzB"}
|
|
@@ -4,8 +4,8 @@ import { useState } from 'react';
|
|
|
4
4
|
import { cn } from '../../lib/cn.js';
|
|
5
5
|
import { Button } from '../Button.js';
|
|
6
6
|
import { Input } from '../Input.js';
|
|
7
|
-
import { NativeSelect } from '../NativeSelect.js';
|
|
8
7
|
import { Popover } from '../Popover.js';
|
|
8
|
+
import { SelectField } from '../SelectField.js';
|
|
9
9
|
import { countActiveConditions, describeFilterQuery, EMPTY_FILTER_QUERY, FILTER_OP_LABELS, newCondition, newGroup, VALUELESS_OPS, } from './filterQuery.js';
|
|
10
10
|
import { Surface } from '../Surface.js';
|
|
11
11
|
import { RAW_BUTTON } from '../../lib/cn.js';
|
|
@@ -77,7 +77,7 @@ export function TableFilterBuilder({ columns, value, onChange, className, defaul
|
|
|
77
77
|
// On a phone the row wraps: column and condition on the first line, the
|
|
78
78
|
// value and × on the second. One line squeezed the column select to
|
|
79
79
|
// nothing and pushed × off the popover (forge task 2593).
|
|
80
|
-
className: 'flex items-center gap-1.5 max-sm:flex-wrap', children: [_jsx("span", { className: 'w-12 shrink-0 text-right text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: conditionIndex === 0 ? 'Where' : 'and' }), _jsx(
|
|
80
|
+
className: 'flex items-center gap-1.5 max-sm:flex-wrap', children: [_jsx("span", { className: 'w-12 shrink-0 text-right text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: conditionIndex === 0 ? 'Where' : 'and' }), _jsx(SelectField, { size: 'sm', "aria-label": 'Column', className: 'min-w-0 flex-1 max-sm:min-w-24', value: condition.column, options: columns.map((c) => ({ value: c.key, label: c.label })), onValueChange: (column) => patchGroup(group.id, (list) => list.map((c) => (c.id === condition.id ? { ...c, column } : c))) }), _jsx(SelectField, { size: 'sm', "aria-label": 'Condition', className: 'w-40 min-w-0 shrink-0 max-sm:w-auto max-sm:min-w-32 max-sm:flex-1', value: condition.op, options: OP_OPTIONS, onValueChange: (op) => patchGroup(group.id, (list) => list.map((c) => (c.id === condition.id ? { ...c, op: op } : c))) }), VALUELESS_OPS.has(condition.op) ? (_jsx("span", { "aria-hidden": true, className: 'w-36 shrink-0 max-sm:hidden' })) : (_jsx(Input, { "aria-label": 'Value', className: 'h-8 w-36 shrink-0 max-sm:ml-13.5 max-sm:w-auto max-sm:min-w-0 max-sm:flex-1', value: condition.value, placeholder: 'value', onChange: (e) => patchGroup(group.id, (list) => list.map((c) => (c.id === condition.id ? { ...c, value: e.target.value } : c))) })), _jsx("button", {
|
|
81
81
|
// guardrails-ignore no-raw-action-button: removes a filter row, mutates no record.
|
|
82
82
|
type: 'button', "aria-label": `Remove condition ${conditionIndex + 1}`, onClick: () => {
|
|
83
83
|
const remaining = group.conditions.filter((c) => c.id !== condition.id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableFilterBuilder.js","sourceRoot":"","sources":["../../../../src/react/components/DataTable/TableFilterBuilder.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"TableFilterBuilder.js","sourceRoot":"","sources":["../../../../src/react/components/DataTable/TableFilterBuilder.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAIhB,YAAY,EACZ,QAAQ,EACR,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgB7C,MAAM,UAAU,GAAI,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5E,KAAK,EAAE,EAAE;IACT,KAAK,EAAE,gBAAgB,CAAC,EAAE,CAAC;CAC5B,CAAC,CAAC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,OAAO,EACP,KAAK,EACL,QAAQ,EACR,SAAS,EACT,WAAW,GAAG,KAAK,GACK;IACxB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC;IAElF,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,IAA0D,EAAE,EAAE,CACjG,QAAQ,CAAC;QACP,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACjC,KAAK,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAChF;KACF,CAAC,CAAC;IAEL,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,OAAO,CACL,KAAC,OAAO,IACN,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,OAAO,EACrB,KAAK,EAAC,OAAO,EACb,KAAK,EAAE,GAAG,EACV,KAAK,EAAE,KAAK,EACZ,OAAO,EACL,MAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,EAChD,QAAQ,EAAE,KAAC,MAAM,KAAG,EACpB,OAAO,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,gBAAgB,uBAGhF,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CACjB,eAAM,SAAS,EAAC,uGAAuG,YACpH,WAAW,GACP,CACR,CAAC,CAAC,CAAC,IAAI,IACD,YAGX,eAAK,SAAS,EAAC,qBAAqB,aAClC,eAAK,SAAS,EAAC,yCAAyC,aACtD,eAAM,SAAS,EAAC,eAAe,uBAAc,EAC7C;4BACE,gFAAgF;4BAChF,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAC3C,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EACnC,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,sLAAsL,CAAC,0BAG1M,IACL,EAEL,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAG3B,aAAG,SAAS,EAAC,yCAAyC,4HAE9B,iBAAQ,SAAS,EAAC,+BAA+B,uBAAgB,EAAC,GAAG,YAEzF,CACL,CAAC,CAAC,CAAC,IAAI,EAGR,cAAK,SAAS,EAAC,8CAA8C,YAC1D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,CACvC,0BACG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAChB,eAAK,SAAS,EAAC,gCAAgC,aAC7C,eAAM,SAAS,EAAC,uBAAuB,GAAG,EAC1C,eAAM,SAAS,EAAC,8HAA8H,mBAEvI,EACP,eAAM,SAAS,EAAC,uBAAuB,GAAG,IACtC,CACP,CAAC,CAAC,CAAC,IAAI,EACR,MAAC,OAAO,IAAC,KAAK,EAAC,MAAM,EAAC,GAAG,EAAC,MAAM,EAAC,MAAM,EAAC,IAAI,EAAC,SAAS,EAAC,iBAAiB,aACtE,cAAK,SAAS,EAAC,uBAAuB,YACnC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,cAAc,EAAE,EAAE,CAAC,CACnD;4CAEE,wEAAwE;4CACxE,oEAAoE;4CACpE,0DAA0D;4CAC1D,SAAS,EAAC,4CAA4C,aAEtD,eAAM,SAAS,EAAC,mGAAmG,YAChH,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAClC,EACP,KAAC,WAAW,IACV,IAAI,EAAC,IAAI,gBACE,QAAQ,EACnB,SAAS,EAAC,gCAAgC,EAC1C,KAAK,EAAE,SAAS,CAAC,MAAM,EACvB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAC/D,aAAa,EAAE,CAAC,MAAM,EAAE,EAAE,CACxB,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAChE,GAEH,EACF,KAAC,WAAW,IACV,IAAI,EAAC,IAAI,gBACE,WAAW,EACtB,SAAS,EAAC,mEAAmE,EAC7E,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,OAAO,EAAE,UAAU,EACnB,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,CACpB,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,EAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC5E,GAEH,EACD,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAGjC,oCAAkB,SAAS,EAAC,6BAA6B,GAAG,CAC7D,CAAC,CAAC,CAAC,CACF,KAAC,KAAK,kBACO,OAAO,EAClB,SAAS,EAAC,6EAA6E,EACvF,KAAK,EAAE,SAAS,CAAC,KAAK,EACtB,WAAW,EAAC,OAAO,EACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC/E,GAEH,CACH,EACD;oDACE,mFAAmF;oDACnF,IAAI,EAAC,QAAQ,gBACD,oBAAoB,cAAc,GAAG,CAAC,EAAE,EACpD,OAAO,EAAE,GAAG,EAAE;wDACZ,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC;wDACxE,wDAAwD;wDACxD,sDAAsD;wDACtD,2CAA2C;wDAC3C,QAAQ,CACN,SAAS,CAAC,MAAM,GAAG,CAAC;4DAClB,CAAC,CAAC;gEACE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7B,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CACxD;6DACF;4DACH,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAC9D,CAAC;oDACJ,CAAC,EACD,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,2KAA2K,CAAC,YAEtM,KAAC,CAAC,IAAC,SAAS,EAAC,QAAQ,GAAG,GACjB,KAxEJ,SAAS,CAAC,EAAE,CAyEb,CACP,CAAC,GACE,EACN;wCACE,gFAAgF;wCAChF,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,EAEtE,SAAS,EAAE,EAAE,CAAC,UAAU,EACtB,8GAA8G,EAC9G,uIAAuI,CACxI,aAED,KAAC,IAAI,IAAC,SAAS,EAAC,QAAQ,GAAG,YACpB,IACD,KAvGF,KAAK,CAAC,EAAE,CAwGZ,CACP,CAAC,GACE,EAEN,cAAK,SAAS,EAAE,EAAE,CAAC,2DAA2D,EAAE,SAAS,CAAC,YACxF,KAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,OAAO,EACf,QAAQ,EAAE,KAAC,IAAI,KAAG,EAClB,OAAO,EACL,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;4BACvB,CAAC,CAAC,yBAAyB;4BAC3B,CAAC,CAAC,kDAAkD,EAExD,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,YAE5E,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,KAAK,GAC/C,GACL,IACF,GACE,CACX,CAAC;AACJ,CAAC"}
|
|
@@ -31,7 +31,7 @@ export interface ListToolbarProps {
|
|
|
31
31
|
chipsAllLabel?: string;
|
|
32
32
|
/** Any further FILTER controls — they sit left, after the chips. */
|
|
33
33
|
filters?: ReactNode;
|
|
34
|
-
/** The sort control (a `
|
|
34
|
+
/** The sort control (a `SelectField`, a `ChoiceMenu`, …). Right-hand cluster. */
|
|
35
35
|
sort?: ReactNode;
|
|
36
36
|
/** The density/view-mode control — normally a `TableViewToggle`. Right-hand cluster. */
|
|
37
37
|
view?: ReactNode;
|
|
@@ -91,7 +91,7 @@ export interface ListToolbarProps {
|
|
|
91
91
|
* chips={buckets}
|
|
92
92
|
* chipValue={filter}
|
|
93
93
|
* onChipChange={(v) => setFilter(typeof v === 'string' ? v : null)}
|
|
94
|
-
* sort={<
|
|
94
|
+
* sort={<SelectField size='sm' aria-label='Sort the queue' … />}
|
|
95
95
|
* view={<TableViewToggle value={view.mode} onChange={view.setMode} />}
|
|
96
96
|
* />
|
|
97
97
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListToolbar.d.ts","sourceRoot":"","sources":["../../../src/react/components/ListToolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGxE,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;IACrC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,
|
|
1
|
+
{"version":3,"file":"ListToolbar.d.ts","sourceRoot":"","sources":["../../../src/react/components/ListToolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGxE,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;IACrC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,iFAAiF;IACjF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,wFAAwF;IACxF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,KAAK,EACL,SAAgB,EAChB,YAAY,EACZ,UAAkB,EAClB,aAAa,EACb,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,SAAS,GACV,EAAE,gBAAgB,+BAsDlB"}
|
|
@@ -54,7 +54,7 @@ import { SearchInput } from './SearchInput.js';
|
|
|
54
54
|
* chips={buckets}
|
|
55
55
|
* chipValue={filter}
|
|
56
56
|
* onChipChange={(v) => setFilter(typeof v === 'string' ? v : null)}
|
|
57
|
-
* sort={<
|
|
57
|
+
* sort={<SelectField size='sm' aria-label='Sort the queue' … />}
|
|
58
58
|
* view={<TableViewToggle value={view.mode} onChange={view.setMode} />}
|
|
59
59
|
* />
|
|
60
60
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOutsidePress.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useOutsidePress.ts"],"names":[],"mappings":"AAEA,KAAK,YAAY,GAAG;IAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAE7E,MAAM,WAAW,sBAAsB;IACrC,+BAA+B;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,+EAA+E;IAC/E,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;CACpC;AAiBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,SAAS,EACT,MAAW,EACX,MAAM,EAAE,aAAoB,EAC5B,KAAa,EACb,QAAkB,GACnB,EAAE,sBAAsB,GAAG;IAAE,cAAc,EAAE,MAAM,IAAI,CAAC;IAAC,oBAAoB,EAAE,MAAM,IAAI,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"useOutsidePress.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useOutsidePress.ts"],"names":[],"mappings":"AAEA,KAAK,YAAY,GAAG;IAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAE7E,MAAM,WAAW,sBAAsB;IACrC,+BAA+B;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,+EAA+E;IAC/E,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;CACpC;AAiBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,SAAS,EACT,MAAW,EACX,MAAM,EAAE,aAAoB,EAC5B,KAAa,EACb,QAAkB,GACnB,EAAE,sBAAsB,GAAG;IAAE,cAAc,EAAE,MAAM,IAAI,CAAC;IAAC,oBAAoB,EAAE,MAAM,IAAI,CAAA;CAAE,CAsG3F"}
|
|
@@ -84,8 +84,24 @@ export function useOutsidePress({ open, onDismiss, inside = [], escape: closeOnE
|
|
|
84
84
|
swallowUntil = Date.now() + 1000;
|
|
85
85
|
decideLater(target);
|
|
86
86
|
};
|
|
87
|
+
// `click` mode: where the press BEGAN. A Radix Select opens on the pointerdown and makes the
|
|
88
|
+
// page inert, so that same press's release — and the click it makes — land on `<html>`; read
|
|
89
|
+
// as outside, it shut the filter builder the moment its Column picker opened (forge task
|
|
90
|
+
// 3064). A click whose press began inside (in the DOM, or this panel's React tree — the
|
|
91
|
+
// panel's `onPointerDownCapture` runs after this capture listener) is inside.
|
|
92
|
+
let pressBegan = null;
|
|
93
|
+
const onPressBegins = (event) => {
|
|
94
|
+
pressInsideRef.current = false;
|
|
95
|
+
pressBegan = event.target instanceof Node ? event.target : null;
|
|
96
|
+
};
|
|
87
97
|
const onClick = (event) => {
|
|
88
98
|
const target = event.target instanceof Node ? event.target : null;
|
|
99
|
+
if (decideOn === 'click') {
|
|
100
|
+
const began = pressBegan;
|
|
101
|
+
pressBegan = null;
|
|
102
|
+
if (began && (pressInsideRef.current || isInside(began) || onAnotherLayer(began)))
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
89
105
|
if (decideOn === 'pointerdown') {
|
|
90
106
|
if (Date.now() < swallowUntil) {
|
|
91
107
|
swallowUntil = 0;
|
|
@@ -108,13 +124,12 @@ export function useOutsidePress({ open, onDismiss, inside = [], escape: closeOnE
|
|
|
108
124
|
latest.current.onDismiss();
|
|
109
125
|
};
|
|
110
126
|
document.addEventListener('click', onClick, true);
|
|
111
|
-
|
|
112
|
-
document.addEventListener('pointerdown', onPointerDown, true);
|
|
127
|
+
document.addEventListener('pointerdown', decideOn === 'pointerdown' ? onPointerDown : onPressBegins, true);
|
|
113
128
|
if (closeOnEscape)
|
|
114
129
|
document.addEventListener('keydown', onKeyDown);
|
|
115
130
|
return () => {
|
|
116
131
|
document.removeEventListener('click', onClick, true);
|
|
117
|
-
document.removeEventListener('pointerdown', onPointerDown, true);
|
|
132
|
+
document.removeEventListener('pointerdown', decideOn === 'pointerdown' ? onPointerDown : onPressBegins, true);
|
|
118
133
|
document.removeEventListener('keydown', onKeyDown);
|
|
119
134
|
for (const timer of timers)
|
|
120
135
|
clearTimeout(timer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOutsidePress.js","sourceRoot":"","sources":["../../../src/react/hooks/useOutsidePress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAmC1C;;;;;;;;GAQG;AACH,MAAM,cAAc,GAAG,0EAA0E,CAAC;AAElG,SAAS,OAAO,CAAC,MAAoB;IACnC,OAAO,CAAC,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;AAC3E,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,IAAI,EACJ,SAAS,EACT,MAAM,GAAG,EAAE,EACX,MAAM,EAAE,aAAa,GAAG,IAAI,EAC5B,KAAK,GAAG,KAAK,EACb,QAAQ,GAAG,OAAO,GACK;IACvB,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,0FAA0F;IAC1F,2CAA2C;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,OAAO,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiC,CAAC;QACxD,MAAM,QAAQ,GAAG,CAAC,IAAU,EAAE,EAAE,CAC9B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnF;;;;WAIG;QACH,MAAM,cAAc,GAAG,CAAC,IAAU,EAAE,EAAE;YACpC,MAAM,KAAK,GAAG,IAAI,YAAY,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;YACnH,IAAI,CAAC,KAAK,EAAE,WAAW;gBAAE,OAAO,KAAK,CAAC;YACtC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;gBAChC,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACnD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,uEAAuE;QACvE,MAAM,WAAW,GAAG,CAAC,MAAmB,EAAE,EAAE;YAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACrB,IAAI,cAAc,CAAC,OAAO,IAAI,CAAC,MAAM;oBAAE,OAAO;gBAC9C,IAAI,QAAQ,CAAC,MAAM,CAAC;oBAAE,OAAO;gBAC7B,IAAI,cAAc,CAAC,MAAM,CAAC;oBAAE,OAAO;gBACnC,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC7B,CAAC,EAAE,CAAC,CAAC,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,CAAC,MAAmB,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAc,CAAC,IAAI,CAAC,cAAc,CAAC,MAAc,CAAC,CAAC;QAEzH,yFAAyF;QACzF,4EAA4E;QAC5E,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,MAAM,aAAa,GAAG,CAAC,KAAmB,EAAE,EAAE;YAC5C,cAAc,CAAC,OAAO,GAAG,KAAK,CAAC;YAC/B,YAAY,GAAG,CAAC,CAAC;YACjB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YAClE,IAAI,OAAO,CAAC,MAAM,CAAC;gBAAE,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;YACtD,WAAW,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,CAAC,KAAiB,EAAE,EAAE;YACpC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YAClE,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;gBAC/B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,EAAE,CAAC;oBAC9B,YAAY,GAAG,CAAC,CAAC;oBACjB,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,KAAK,CAAC,eAAe,EAAE,CAAC;gBAC1B,CAAC;gBACD,OAAO;YACT,CAAC;YACD,cAAc,CAAC,OAAO,GAAG,KAAK,CAAC;YAC/B,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5C,kFAAkF;gBAClF,gFAAgF;gBAChF,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,eAAe,EAAE,CAAC;YAC1B,CAAC;YACD,WAAW,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC,CAAC;QACF,MAAM,SAAS,GAAG,CAAC,KAAoB,EAAE,EAAE;YACzC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ;gBAAE,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QACzD,CAAC,CAAC;QAEF,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAClD,
|
|
1
|
+
{"version":3,"file":"useOutsidePress.js","sourceRoot":"","sources":["../../../src/react/hooks/useOutsidePress.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,9 +52,10 @@ export interface ScrollMemoryOptions {
|
|
|
52
52
|
*/
|
|
53
53
|
enabled?: boolean;
|
|
54
54
|
/**
|
|
55
|
-
* How long a restore waits for the page to
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* How long a restore waits for the page to GROW before settling as close as it got — counted
|
|
56
|
+
* from the last time it grew, so a list paging itself down keeps being followed (up to 20s in
|
|
57
|
+
* all). Default 3000ms — a reload, where only the first page of a list exists, needs a few
|
|
58
|
+
* pages' round trips, each of which must land within it.
|
|
58
59
|
*/
|
|
59
60
|
deadlineMs?: number;
|
|
60
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollMemory.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useScrollMemory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA2B,MAAM,OAAO,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,IAAI,CA0BzF;AAwED,oGAAoG;AACpG,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC;AAEzG,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB
|
|
1
|
+
{"version":3,"file":"useScrollMemory.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useScrollMemory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA2B,MAAM,OAAO,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,IAAI,CA0BzF;AAwED,oGAAoG;AACpG,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC;AAEzG,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAqDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAIzD"}
|
|
@@ -122,6 +122,8 @@ function begin(scroller, location, last, deadlineMs) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
const RESTORE_DEADLINE_MS = 3_000;
|
|
125
|
+
/** The longest a restore keeps following a page that is still growing — the viewer can always take it back sooner. */
|
|
126
|
+
const RESTORE_CEILING_MS = 20_000;
|
|
125
127
|
/**
|
|
126
128
|
* How long a landed restore holds its target against a scroll the viewer did not make. Long
|
|
127
129
|
* enough for a list to finish settling (the late move in forge task 2784 came inside 800ms, under
|
|
@@ -215,7 +217,14 @@ function file(id, y) {
|
|
|
215
217
|
* cancel that reports nothing. `settled` is called once, when it lands or gives up.
|
|
216
218
|
*/
|
|
217
219
|
function restore(scroller, target, deadlineMs, settled) {
|
|
218
|
-
|
|
220
|
+
// 🔴 The deadline is for a page that has STOPPED growing, not a stopwatch from the start (forge
|
|
221
|
+
// task 3069): flix's phone grid, reloaded and coming back to 30,000px, pages itself down one round
|
|
222
|
+
// trip at a time, and with eight browsers sharing the Mac those trips took longer than 3s in all.
|
|
223
|
+
// The restore gave up at 29,731px while the pages were still arriving — a slow phone network is
|
|
224
|
+
// the same case. Each time the page grows the wait starts again, up to RESTORE_CEILING_MS.
|
|
225
|
+
const started = performance.now();
|
|
226
|
+
let deadline = started + deadlineMs;
|
|
227
|
+
let lastRoom = -1;
|
|
219
228
|
let frame = 0;
|
|
220
229
|
let holdTimer;
|
|
221
230
|
let phase = 'waiting';
|
|
@@ -252,7 +261,12 @@ function restore(scroller, target, deadlineMs, settled) {
|
|
|
252
261
|
}
|
|
253
262
|
// Short of it: go as far as the page allows — that is what asks an infinite list for more.
|
|
254
263
|
scroller.set(Math.max(0, room));
|
|
255
|
-
|
|
264
|
+
const now = performance.now();
|
|
265
|
+
if (room > lastRoom) {
|
|
266
|
+
lastRoom = room;
|
|
267
|
+
deadline = Math.min(now + deadlineMs, started + Math.max(deadlineMs, RESTORE_CEILING_MS));
|
|
268
|
+
}
|
|
269
|
+
if (now >= deadline) {
|
|
256
270
|
finish();
|
|
257
271
|
settled('deadline');
|
|
258
272
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollMemory.js","sourceRoot":"","sources":["../../../src/react/hooks/useScrollMemory.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"useScrollMemory.js","sourceRoot":"","sources":["../../../src/react/hooks/useScrollMemory.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "./focusViewer.css";
|
|
2
|
+
import { type GifSection } from "./gifClip.js";
|
|
2
3
|
/**
|
|
3
4
|
* One still already kept beside this video.
|
|
4
5
|
*
|
|
@@ -45,6 +46,12 @@ export interface FocusViewerProps {
|
|
|
45
46
|
}) => Promise<FocusSaveResult | void> | FocusSaveResult | void;
|
|
46
47
|
/** An arbitrary control in the action row — collections puts its folder picker here. */
|
|
47
48
|
destination?: React.ReactNode;
|
|
49
|
+
/**
|
|
50
|
+
* Keep a looping GIF of a section. OMIT IT and "Make a GIF" still works — it downloads the
|
|
51
|
+
* file instead, because a GIF needs no store to be useful the way a filed still does.
|
|
52
|
+
* `destination` applies to it as it does to a snapshot.
|
|
53
|
+
*/
|
|
54
|
+
onSaveGif?: (gif: Blob, meta: GifSection) => Promise<FocusSaveResult | void> | FocusSaveResult | void;
|
|
48
55
|
/**
|
|
49
56
|
* How tall, in CSS pixels, the picture in the host's own player was when this opened.
|
|
50
57
|
*
|
|
@@ -67,5 +74,5 @@ export interface FocusViewerProps {
|
|
|
67
74
|
atSeconds: number;
|
|
68
75
|
}) => void;
|
|
69
76
|
}
|
|
70
|
-
export declare function FocusViewer({ videoUrl, title, startAt, durationSeconds, snapshots, onSaveSnapshot, destination, matchHeight, videoId, onClose, }: FocusViewerProps): React.ReactNode;
|
|
77
|
+
export declare function FocusViewer({ videoUrl, title, startAt, durationSeconds, snapshots, onSaveSnapshot, destination, onSaveGif, matchHeight, videoId, onClose, }: FocusViewerProps): React.ReactNode;
|
|
71
78
|
//# sourceMappingURL=FocusViewer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FocusViewer.d.ts","sourceRoot":"","sources":["../../../src/react/media/FocusViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"FocusViewer.d.ts","sourceRoot":"","sources":["../../../src/react/media/FocusViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,CAAC;AAgE3B,OAAO,EAMN,KAAK,UAAU,EAIf,MAAM,cAAc,CAAC;AAuDtB;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,6FAA6F;AAC7F,MAAM,WAAW,eAAe;IAC/B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAChC,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kFAAkF;IAClF,SAAS,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACrC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAChB,IAAI,EAAE,IAAI,EACV,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAEvB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,eAAe,GAAG,IAAI,CAAC;IAC9D,wFAAwF;IACxF,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,CACX,GAAG,EAAE,IAAI,EACT,IAAI,EAAE,UAAU,KAEZ,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,eAAe,GAAG,IAAI,CAAC;IAC9D;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,OAAO,EAAE,CAAC,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClD;AAQD,wBAAgB,WAAW,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,OAAW,EACX,eAAe,EACf,SAAc,EACd,cAAc,EACd,WAAW,EACX,SAAS,EACT,WAAW,EACX,OAAO,EACP,OAAO,GACP,EAAE,gBAAgB,GAAG,KAAK,CAAC,SAAS,CAwgCpC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import "./focusViewer.css";
|
|
3
3
|
/**
|
|
4
4
|
* The focus viewer — look closely at PART of a video, and keep what you find.
|
|
@@ -18,6 +18,10 @@ import "./focusViewer.css";
|
|
|
18
18
|
* which is the correct surface for a host with nowhere to put one.
|
|
19
19
|
* · `destination` — an arbitrary node in the action row, for a host that wants to choose
|
|
20
20
|
* WHERE (collections offers its folder picker there; binary-server offers nothing).
|
|
21
|
+
* · `onSaveGif` — where a looping GIF of a section goes. Unlike a snapshot, a GIF is
|
|
22
|
+
* offered to EVERY host: without a store it downloads to the device (owner, 2026-10-11:
|
|
23
|
+
* *"In collections and the cb media player in general allow making a looping gif out of a
|
|
24
|
+
* section of a video"* — "in general" is binary-server too, which keeps no stills).
|
|
21
25
|
* Everything else — geometry, gestures, keyboard, the canvas capture — is the same code for
|
|
22
26
|
* every consumer, which is the entire point of the lift.
|
|
23
27
|
*
|
|
@@ -51,7 +55,7 @@ import "./focusViewer.css";
|
|
|
51
55
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
52
56
|
import { Alert } from "../components/Alert.js";
|
|
53
57
|
import { Button } from "../components/Button.js";
|
|
54
|
-
import {
|
|
58
|
+
import { SelectField } from "../components/SelectField.js";
|
|
55
59
|
import { Tooltip } from "../components/Tooltip.js";
|
|
56
60
|
import { MediaResizeBar, MediaResizeCorner, useMediaResize } from "./MediaResize.js";
|
|
57
61
|
import { FOCUS_HEIGHT_KEY } from "./stageResize.js";
|
|
@@ -59,9 +63,23 @@ import { PictureControls } from "./PictureControls.js";
|
|
|
59
63
|
import { handOffPlayhead } from "./playheadHandoff.js";
|
|
60
64
|
import { isPlayPauseSpace } from "./playerSpaceKey.js";
|
|
61
65
|
import { PLAYBACK_RATES } from "./VideoPlayer.js";
|
|
66
|
+
import { clampGifSection, formatGifBytes, formatGifTime, GIF_FRAME_RATES, GIF_WIDTHS, gifFileName, gifSectionAt, MAX_GIF_SECONDS, } from "./gifClip.js";
|
|
62
67
|
import { FULL_REGION, MAX_ZOOM, composeSelection, fillRegion, fitBox, isFullRegion, normalizeRect, panRegion, regionAtZoom, regionLayout, regionPixels, regionZoom, stageAspectFor, zoomRegion, } from "./videoRegion.js";
|
|
63
68
|
import { NO_ENHANCE, UPSCALE_FACTORS, filterString, isEnhanced, readEnhance, snapshotSize, writeEnhance, } from "./videoEnhance.js";
|
|
64
69
|
import { cn, RAW_BUTTON } from '../lib/cn.js';
|
|
70
|
+
/**
|
|
71
|
+
* The GIF recorder and its encoder, fetched the first time the GIF panel opens — they are this
|
|
72
|
+
* feature's bytes, and a gallery that never makes a GIF should not carry them. Kept once
|
|
73
|
+
* fetched; a failed fetch is forgotten so the next press asks again.
|
|
74
|
+
*/
|
|
75
|
+
let gifRecorder = null;
|
|
76
|
+
function loadGifRecorder() {
|
|
77
|
+
gifRecorder ??= import("./gifRecord.js").catch((error) => {
|
|
78
|
+
gifRecorder = null;
|
|
79
|
+
throw error;
|
|
80
|
+
});
|
|
81
|
+
return gifRecorder;
|
|
82
|
+
}
|
|
65
83
|
/** `m:ss`, or `h:mm:ss` past the hour. Local so this module has no app dependency. */
|
|
66
84
|
function formatDuration(seconds) {
|
|
67
85
|
if (!Number.isFinite(seconds) || seconds < 0)
|
|
@@ -77,7 +95,7 @@ function formatDuration(seconds) {
|
|
|
77
95
|
const ZOOM_STEP = 1.25;
|
|
78
96
|
/** One frame at 30 fps — see `stepFrame` for why this is a constant and not measured. */
|
|
79
97
|
const FRAME_STEP_SECONDS = 1 / 30;
|
|
80
|
-
export function FocusViewer({ videoUrl, title, startAt = 0, durationSeconds, snapshots = [], onSaveSnapshot, destination, matchHeight, videoId, onClose, }) {
|
|
98
|
+
export function FocusViewer({ videoUrl, title, startAt = 0, durationSeconds, snapshots = [], onSaveSnapshot, destination, onSaveGif, matchHeight, videoId, onClose, }) {
|
|
81
99
|
const videoRef = useRef(null);
|
|
82
100
|
const viewportRef = useRef(null);
|
|
83
101
|
/*
|
|
@@ -122,6 +140,19 @@ export function FocusViewer({ videoUrl, title, startAt = 0, durationSeconds, sna
|
|
|
122
140
|
const [busy, setBusy] = useState(false);
|
|
123
141
|
const [error, setError] = useState(null);
|
|
124
142
|
const [note, setNote] = useState(null);
|
|
143
|
+
/*
|
|
144
|
+
* ── A section, as a looping GIF ────────────────────────────────────────────────────
|
|
145
|
+
* Null while the GIF panel is shut. Open, it is the section the GIF will hold, and while
|
|
146
|
+
* `loopSection` is on the picture plays only that section, round and round — the preview
|
|
147
|
+
* IS the GIF, before a frame is encoded.
|
|
148
|
+
*/
|
|
149
|
+
const [gif, setGif] = useState(null);
|
|
150
|
+
const [loopSection, setLoopSection] = useState(true);
|
|
151
|
+
const [gifFps, setGifFps] = useState(15);
|
|
152
|
+
const [gifWidth, setGifWidth] = useState(480);
|
|
153
|
+
/** `[done, total]` frames while one is being made. */
|
|
154
|
+
const [gifProgress, setGifProgress] = useState(null);
|
|
155
|
+
const gifAbort = useRef(null);
|
|
125
156
|
/**
|
|
126
157
|
* Keep the measured viewport current.
|
|
127
158
|
*
|
|
@@ -252,6 +283,111 @@ export function FocusViewer({ videoUrl, title, startAt = 0, durationSeconds, sna
|
|
|
252
283
|
if (wasPlaying)
|
|
253
284
|
void video.play().catch(() => undefined);
|
|
254
285
|
}, []);
|
|
286
|
+
/**
|
|
287
|
+
* Play the GIF's section on a loop while it is being chosen.
|
|
288
|
+
*
|
|
289
|
+
* Checked every animation frame rather than on `timeupdate`, which fires about four times
|
|
290
|
+
* a second and would let the picture run a quarter-second past the end before snapping
|
|
291
|
+
* back — a preview that shows frames the GIF will not have. Only while PLAYING: a paused
|
|
292
|
+
* scrub or frame-step outside the section is the person looking for a new end, and must not
|
|
293
|
+
* be yanked back. Stands down while a GIF is being made, whose seeks are its own.
|
|
294
|
+
*/
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
if (!gif || !loopSection || gifProgress)
|
|
297
|
+
return;
|
|
298
|
+
let raf = 0;
|
|
299
|
+
const check = () => {
|
|
300
|
+
const video = videoRef.current;
|
|
301
|
+
if (video && !video.paused && !scrubbing.current) {
|
|
302
|
+
const t = video.currentTime;
|
|
303
|
+
if (t >= gif.endSeconds || t < gif.startSeconds - 0.05)
|
|
304
|
+
video.currentTime = gif.startSeconds;
|
|
305
|
+
}
|
|
306
|
+
raf = requestAnimationFrame(check);
|
|
307
|
+
};
|
|
308
|
+
raf = requestAnimationFrame(check);
|
|
309
|
+
return () => cancelAnimationFrame(raf);
|
|
310
|
+
}, [gif, loopSection, gifProgress]);
|
|
311
|
+
/** Close the GIF panel, stopping a GIF still being made. */
|
|
312
|
+
const closeGif = () => {
|
|
313
|
+
gifAbort.current?.abort();
|
|
314
|
+
setGif(null);
|
|
315
|
+
};
|
|
316
|
+
/** Mark where the GIF starts or ends at the playhead — the `I` and `O` keys, as in an editor. */
|
|
317
|
+
const markGif = (end) => {
|
|
318
|
+
const at = videoRef.current?.currentTime ?? position;
|
|
319
|
+
const total = duration || videoRef.current?.duration || 0;
|
|
320
|
+
// Fetched while the section is chosen, so "Save GIF" does not wait on the network.
|
|
321
|
+
void loadGifRecorder().catch(() => undefined);
|
|
322
|
+
setGif((current) => {
|
|
323
|
+
if (!current)
|
|
324
|
+
return gifSectionAt(at, total);
|
|
325
|
+
const length = current.endSeconds - current.startSeconds;
|
|
326
|
+
return end === "start"
|
|
327
|
+
? // A start past the old end keeps the section's length rather than collapsing it.
|
|
328
|
+
clampGifSection({ startSeconds: at, endSeconds: at >= current.endSeconds ? at + length : current.endSeconds }, total, "start")
|
|
329
|
+
: clampGifSection({ startSeconds: current.startSeconds, endSeconds: at }, total, "end");
|
|
330
|
+
});
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* Make the GIF: seek through the section, encode what is ON SCREEN — the zoomed area and
|
|
334
|
+
* the picture adjustments, exactly as a snapshot takes them — and save or download it.
|
|
335
|
+
*/
|
|
336
|
+
const makeGif = async (how) => {
|
|
337
|
+
const video = videoRef.current;
|
|
338
|
+
if (!video || !gif)
|
|
339
|
+
return;
|
|
340
|
+
const abort = new AbortController();
|
|
341
|
+
gifAbort.current = abort;
|
|
342
|
+
setError(null);
|
|
343
|
+
setNote(null);
|
|
344
|
+
setGifProgress([0, 1]);
|
|
345
|
+
try {
|
|
346
|
+
const crop = regionPixels(shown, { width: video.videoWidth, height: video.videoHeight });
|
|
347
|
+
if (!crop)
|
|
348
|
+
throw new Error("The video has not decoded a frame yet.");
|
|
349
|
+
// 🔴 A failed fetch here is almost always a deploy since this page opened — the chunk
|
|
350
|
+
// was renamed — and says so, rather than reloading away the section being made.
|
|
351
|
+
const { recordGifClip } = await loadGifRecorder().catch(() => {
|
|
352
|
+
throw new Error("This app was updated since the page opened — reload it to make a GIF.");
|
|
353
|
+
});
|
|
354
|
+
const blob = await recordGifClip(video, {
|
|
355
|
+
...gif,
|
|
356
|
+
fps: gifFps,
|
|
357
|
+
width: gifWidth,
|
|
358
|
+
crop,
|
|
359
|
+
...(isEnhanced(enhance) ? { filter: filterString(enhance) } : {}),
|
|
360
|
+
onProgress: (done, total) => setGifProgress([done, total]),
|
|
361
|
+
signal: abort.signal,
|
|
362
|
+
});
|
|
363
|
+
const weight = formatGifBytes(blob.size);
|
|
364
|
+
if (how === "save" && onSaveGif) {
|
|
365
|
+
const saved = (await onSaveGif(blob, gif)) ?? {};
|
|
366
|
+
setNote(saved.message
|
|
367
|
+
? `${saved.message} (${weight} GIF)`
|
|
368
|
+
: saved.duplicate
|
|
369
|
+
? "That exact GIF was already saved — nothing added."
|
|
370
|
+
: `GIF saved — ${weight}.`);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
downloadBlob(blob, gifFileName(title, gif.startSeconds));
|
|
374
|
+
setNote(`GIF downloaded — ${weight}.`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
if (err instanceof DOMException && err.name === "AbortError")
|
|
379
|
+
setNote("GIF cancelled.");
|
|
380
|
+
else
|
|
381
|
+
setError(err instanceof Error ? err.message : "The GIF could not be made.");
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
if (gifAbort.current === abort)
|
|
385
|
+
gifAbort.current = null;
|
|
386
|
+
setGifProgress(null);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
// A GIF still being made is stopped when the viewer goes.
|
|
390
|
+
useEffect(() => () => gifAbort.current?.abort(), []);
|
|
255
391
|
/**
|
|
256
392
|
* Drag the timeline to skip around LIVE.
|
|
257
393
|
*
|
|
@@ -496,7 +632,11 @@ export function FocusViewer({ videoUrl, title, startAt = 0, durationSeconds, sna
|
|
|
496
632
|
event.altKey ||
|
|
497
633
|
event.shiftKey)
|
|
498
634
|
return;
|
|
499
|
-
if (event.key === "
|
|
635
|
+
if (gif && !gifProgress && (event.key === "i" || event.key === "o")) {
|
|
636
|
+
event.preventDefault();
|
|
637
|
+
markGif(event.key === "i" ? "start" : "end");
|
|
638
|
+
}
|
|
639
|
+
else if (event.key === "ArrowRight") {
|
|
500
640
|
event.preventDefault();
|
|
501
641
|
stepFrame(1);
|
|
502
642
|
}
|
|
@@ -587,12 +727,12 @@ export function FocusViewer({ videoUrl, title, startAt = 0, durationSeconds, sna
|
|
|
587
727
|
}, onKeyDown: beginScrub, onKeyUp: endScrub,
|
|
588
728
|
// Pause for the duration of a drag — see `beginScrub` for why playback and a
|
|
589
729
|
// drag otherwise fight over `currentTime`.
|
|
590
|
-
onPointerDown: beginScrub, step: 0.02, type: "range", value: position }), _jsxs("span", { className: "inspector__time", children: [formatDuration(position) || "0:00", " / ", formatDuration(duration) || "0:00"] }), _jsxs("label", { className: "inspector__speed", htmlFor: "inspector-speed", children: [_jsx("span", { className: "sr-only", children: "Playback speed" }), _jsx(
|
|
591
|
-
const next = Number(
|
|
730
|
+
onPointerDown: beginScrub, step: 0.02, type: "range", value: position }), _jsxs("span", { className: "inspector__time", children: [formatDuration(position) || "0:00", " / ", formatDuration(duration) || "0:00"] }), _jsxs("label", { className: "inspector__speed", htmlFor: "inspector-speed", children: [_jsx("span", { className: "sr-only", children: "Playback speed" }), _jsx(SelectField, { id: "inspector-speed", className: "w-auto", onValueChange: (value) => {
|
|
731
|
+
const next = Number(value);
|
|
592
732
|
setRate(next);
|
|
593
733
|
if (videoRef.current)
|
|
594
734
|
videoRef.current.playbackRate = next;
|
|
595
|
-
},
|
|
735
|
+
}, options: PLAYBACK_RATES.map((option) => ({ value: String(option), label: `${option}×` })), size: "sm", value: String(rate) })] })] }), _jsxs("div", { className: "inspector__tools", children: [_jsx(Button, { onClick: () => setRegion((current) => zoomRegion(current, 1 / ZOOM_STEP)), tooltip: "Wider", variant: "secondary", children: "\uFF0D" }), _jsxs("label", { className: "inspector__zoom", children: [_jsx("span", { className: "sr-only", children: "Zoom" }), _jsx(Tooltip, { content: "How close in you are looking", children: _jsx("input", {
|
|
596
736
|
// guardrails-ignore no-raw-inputs: sibling of the transport — see above
|
|
597
737
|
max: MAX_ZOOM, min: 1, onChange: (event) => setRegion((current) => regionAtZoom(current, Number(event.target.value))), step: 0.1, type: "range", value: Math.min(zoom, MAX_ZOOM), "aria-label": "How close in you are looking" }) }), _jsxs("span", { className: "inspector__time", children: [zoom.toFixed(1), "\u00D7"] })] }), _jsx(Button, { onClick: () => setRegion((current) => zoomRegion(current, ZOOM_STEP)), tooltip: "Closer, on the middle of what you are looking at", variant: "secondary", children: "\uFF0B" }), _jsx(Button, { onClick: () => setSelecting((on) => !on), tooltip: selecting
|
|
598
738
|
? "Stop selecting — a drag pans instead"
|
|
@@ -605,9 +745,20 @@ export function FocusViewer({ videoUrl, title, startAt = 0, durationSeconds, sna
|
|
|
605
745
|
// Upscale is this surface's own concern, which is why Reset is overridden
|
|
606
746
|
// here rather than the control being taught about it.
|
|
607
747
|
setUpscale(1);
|
|
608
|
-
}, resettable: isEnhanced(enhance) || upscale !== 1, value: enhance, children: onSaveSnapshot ? (_jsxs("label", { className: "picture__slider", htmlFor: "inspector-upscale", children: [_jsx("span", { children: "Save at" }), _jsx(
|
|
609
|
-
|
|
610
|
-
|
|
748
|
+
}, resettable: isEnhanced(enhance) || upscale !== 1, value: enhance, children: onSaveSnapshot ? (_jsxs("label", { className: "picture__slider", htmlFor: "inspector-upscale", children: [_jsx("span", { children: "Save at" }), _jsx(SelectField, { id: "inspector-upscale", className: "w-auto", onValueChange: (value) => setUpscale(Number(value)), options: UPSCALE_FACTORS.map((factor) => ({
|
|
749
|
+
value: String(factor),
|
|
750
|
+
label: `${factor}× ${factor === 1 ? "(as-is)" : "upscaled"}`,
|
|
751
|
+
})), size: "sm", value: String(upscale) })] })) : null }), _jsxs("div", { className: "inspector__save", children: [onSaveSnapshot ? (_jsxs(_Fragment, { children: [_jsx(Button, { disabled: busy, onClick: () => void takeSnapshot(false), tooltip: isFullRegion(region)
|
|
752
|
+
? "Save this frame as a picture"
|
|
753
|
+
: "Save just the part you are looking at", variant: "primary", children: isFullRegion(region) ? "Snapshot this frame" : "Snapshot this area" }), isFullRegion(region) ? null : (_jsx(Button, { disabled: busy, onClick: () => void takeSnapshot(true), tooltip: "Save the whole frame, ignoring the area you selected", variant: "secondary", children: "Snapshot whole frame" }))] })) : null, _jsx(Button, { "aria-expanded": gif !== null, disabled: gifProgress !== null, onClick: () => (gif ? closeGif() : markGif("start")), tooltip: gif
|
|
754
|
+
? "Put the GIF away"
|
|
755
|
+
: "Turn a few seconds of this video into a looping GIF, starting here", variant: gif ? "primary" : "secondary", children: "Make a GIF" }), onSaveSnapshot || onSaveGif ? destination : null] }), gif ? (_jsxs("section", { "aria-label": "Make a GIF", className: "inspector__gif", children: [_jsxs("div", { className: "inspector__gifrange", children: [_jsx(Button, { disabled: gifProgress !== null, onClick: () => markGif("start"), tooltip: "Start the GIF at the playhead (I)", variant: "secondary", children: "Start here" }), _jsxs("span", { className: "inspector__gifspan", children: [_jsx("span", { children: formatGifTime(gif.startSeconds) }), _jsx("span", { "aria-hidden": "true", children: "\u2013" }), _jsx("span", { children: formatGifTime(gif.endSeconds) }), _jsxs("span", { className: "inspector__time", children: [(gif.endSeconds - gif.startSeconds).toFixed(1), " s", gif.endSeconds - gif.startSeconds >= MAX_GIF_SECONDS - 0.05 ? " · the longest a GIF can be" : ""] })] }), _jsx(Button, { disabled: gifProgress !== null, onClick: () => markGif("end"), tooltip: `End the GIF at the playhead (O) — up to ${MAX_GIF_SECONDS} s`, variant: "secondary", children: "End here" }), _jsx(Button, { "aria-pressed": loopSection, disabled: gifProgress !== null, onClick: () => setLoopSection((on) => !on), tooltip: loopSection
|
|
756
|
+
? "Playing only the section, on a loop — press to play the whole video again"
|
|
757
|
+
: "Play only the section, on a loop, the way the GIF will", variant: loopSection ? "primary" : "ghost", children: "Loop section" })] }), _jsxs("div", { className: "inspector__gifout", children: [_jsxs("label", { className: "picture__slider", htmlFor: "inspector-gif-fps", children: [_jsx("span", { children: "Smoothness" }), _jsx(SelectField, { id: "inspector-gif-fps", className: "w-auto", disabled: gifProgress !== null, onValueChange: (value) => setGifFps(Number(value)), options: GIF_FRAME_RATES.map((fps) => ({ value: String(fps), label: `${fps} fps` })), size: "sm", value: String(gifFps) })] }), _jsxs("label", { className: "picture__slider", htmlFor: "inspector-gif-width", children: [_jsx("span", { children: "Width" }), _jsx(SelectField, { id: "inspector-gif-width", className: "w-auto", disabled: gifProgress !== null, onValueChange: (value) => setGifWidth(Number(value)), options: GIF_WIDTHS.map((width) => ({ value: String(width), label: `${width} px` })), size: "sm", value: String(gifWidth) })] }), gifProgress ? (_jsxs(_Fragment, { children: [_jsxs("span", { "aria-live": "polite", className: "inspector__time", children: ["Making the GIF \u2014 frame ", gifProgress[0], " of ", gifProgress[1]] }), _jsx(Button, { onClick: () => gifAbort.current?.abort(), tooltip: "Stop making this GIF", variant: "ghost", children: "Cancel" })] })) : (_jsxs(_Fragment, { children: [onSaveGif ? (_jsx(Button, { onClick: () => void makeGif("save"), tooltip: isFullRegion(region)
|
|
758
|
+
? "Save the section as a looping GIF beside this video"
|
|
759
|
+
: "Save the section as a looping GIF — just the part you are looking at", variant: "primary", children: "Save GIF" })) : null, _jsx(Button, { onClick: () => void makeGif("download"), tooltip: isFullRegion(region)
|
|
760
|
+
? "Download the section as a looping GIF"
|
|
761
|
+
: "Download the section as a looping GIF — just the part you are looking at", variant: onSaveGif ? "secondary" : "primary", children: onSaveGif ? "Download" : "Download GIF" })] }))] })] })) : null, error ? _jsx(Alert, { tone: "error", children: error }) : null, note && !error ? _jsx(Alert, { tone: "success", children: note }) : null, snapshots.length > 0 ? (_jsxs("div", { className: "inspector__strip", children: [_jsx("p", { className: "inspector__striphead", children: "Stills from this video \u2014 click one to jump to that moment" }), _jsx("ul", { className: "inspector__stills", children: snapshots.map((still) => (_jsx("li", { children: _jsx(SnapshotChip, { onJump: () => seekTo(still.atSeconds ?? 0), still: still }) }, still.id))) })] })) : null] }));
|
|
611
762
|
}
|
|
612
763
|
/**
|
|
613
764
|
* One still in the strip. Its thumbnail is fetched lazily and per item, exactly like a
|
|
@@ -646,4 +797,17 @@ function marqueeStyle(rect) {
|
|
|
646
797
|
height: `${box.h * 100}%`,
|
|
647
798
|
};
|
|
648
799
|
}
|
|
800
|
+
/** Hand a made file to the browser's own download — the GIF of a host with no store. */
|
|
801
|
+
function downloadBlob(blob, name) {
|
|
802
|
+
const url = URL.createObjectURL(blob);
|
|
803
|
+
const link = document.createElement("a");
|
|
804
|
+
link.href = url;
|
|
805
|
+
link.download = name;
|
|
806
|
+
link.rel = "noopener";
|
|
807
|
+
document.body.append(link);
|
|
808
|
+
link.click();
|
|
809
|
+
link.remove();
|
|
810
|
+
// After the click has been handed to the browser; revoking at once cancels it in Safari.
|
|
811
|
+
setTimeout(() => URL.revokeObjectURL(url), 30_000);
|
|
812
|
+
}
|
|
649
813
|
//# sourceMappingURL=FocusViewer.js.map
|