@joewinke/jatui 0.38.6 → 0.40.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/CLAUDE.md +54 -5
- package/THIRD_PARTY_NOTICES.md +67 -0
- package/dist/actions/scrollEdge.d.ts +55 -0
- package/dist/actions/scrollEdge.js +144 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte.d.ts +1 -1
- package/dist/artifact-blocks/blocks/diagram/schema.d.ts +1 -1
- package/dist/components/BottomSheet.svelte +7 -6
- package/dist/components/BottomSheet.svelte.d.ts +2 -0
- package/dist/components/BulkActionBar.svelte +32 -2
- package/dist/components/BulkActionBar.svelte.d.ts +5 -1
- package/dist/components/CloseButton.svelte +75 -0
- package/dist/components/CloseButton.svelte.d.ts +29 -0
- package/dist/components/Drawer.svelte +10 -11
- package/dist/components/Drawer.svelte.d.ts +2 -8
- package/dist/components/FloatingActionBar.svelte +102 -34
- package/dist/components/FloatingActionBar.svelte.d.ts +9 -0
- package/dist/components/IconButton.svelte +200 -0
- package/dist/components/IconButton.svelte.d.ts +46 -0
- package/dist/components/LinkButton.svelte +117 -0
- package/dist/components/LinkButton.svelte.d.ts +32 -0
- package/dist/components/MediaExplorer.svelte +249 -0
- package/dist/components/MediaExplorer.svelte.d.ts +37 -0
- package/dist/components/MediaLightbox.svelte +2 -7
- package/dist/components/Modal.svelte +2 -10
- package/dist/components/NotificationCenter.svelte +481 -0
- package/dist/components/NotificationCenter.svelte.d.ts +29 -0
- package/dist/components/PinInput.svelte +94 -31
- package/dist/components/PinInput.svelte.d.ts +4 -0
- package/dist/components/SearchDropdown.svelte +54 -8
- package/dist/components/SearchDropdown.svelte.d.ts +12 -0
- package/dist/components/TextArea.svelte +67 -5
- package/dist/components/TextArea.svelte.d.ts +4 -6
- package/dist/components/ThemeSelector.svelte +29 -9
- package/dist/components/ThemeSelector.svelte.d.ts +6 -0
- package/dist/components/ThemeToggle.svelte +28 -3
- package/dist/components/ThemeToggle.svelte.d.ts +9 -0
- package/dist/components/agent/AgentLog.svelte +217 -0
- package/dist/components/agent/AgentLog.svelte.d.ts +72 -0
- package/dist/components/agent/AgentLogRow.svelte +281 -0
- package/dist/components/agent/AgentLogRow.svelte.d.ts +21 -0
- package/dist/components/agent/AgentTaskList.svelte +607 -0
- package/dist/components/agent/AgentTaskList.svelte.d.ts +40 -0
- package/dist/components/agent/AgentThinking.svelte +280 -0
- package/dist/components/agent/AgentThinking.svelte.d.ts +22 -0
- package/dist/components/agent/ComposerGlow.svelte +265 -0
- package/dist/components/agent/ComposerGlow.svelte.d.ts +18 -0
- package/dist/components/agent/ContextMeter.svelte +277 -0
- package/dist/components/agent/ContextMeter.svelte.d.ts +21 -0
- package/dist/components/agent/Questionnaire.svelte +767 -0
- package/dist/components/agent/Questionnaire.svelte.d.ts +29 -0
- package/dist/components/agent/ShimmerText.svelte +100 -0
- package/dist/components/agent/ShimmerText.svelte.d.ts +19 -0
- package/dist/components/agent/StreamedText.svelte +135 -0
- package/dist/components/agent/StreamedText.svelte.d.ts +13 -0
- package/dist/components/agent/agentLog.d.ts +22 -0
- package/dist/components/agent/agentLog.js +26 -0
- package/dist/components/agent/agentTaskList.d.ts +90 -0
- package/dist/components/agent/agentTaskList.js +93 -0
- package/dist/components/agent/agentThinking.d.ts +46 -0
- package/dist/components/agent/agentThinking.js +78 -0
- package/dist/components/agent/contextMeter.d.ts +97 -0
- package/dist/components/agent/contextMeter.js +156 -0
- package/dist/components/agent/questionnaire.d.ts +104 -0
- package/dist/components/agent/questionnaire.js +225 -0
- package/dist/components/agent/revealCore.d.ts +74 -0
- package/dist/components/agent/revealCore.js +160 -0
- package/dist/components/agent/revealTicker.d.ts +34 -0
- package/dist/components/agent/revealTicker.js +73 -0
- package/dist/components/agent/streamedText.d.ts +28 -0
- package/dist/components/agent/streamedText.js +59 -0
- package/dist/components/basic/Breadcrumb.svelte +226 -11
- package/dist/components/basic/Breadcrumb.svelte.d.ts +27 -1
- package/dist/components/basic/breadcrumbCollapse.d.ts +19 -0
- package/dist/components/basic/breadcrumbCollapse.js +12 -0
- package/dist/components/graph/GraphCanvas.svelte.d.ts +2 -2
- package/dist/components/messaging/MessageInput.svelte +34 -4
- package/dist/components/messaging/MessageInput.svelte.d.ts +7 -0
- package/dist/components/messaging/MessageItem.svelte +2 -2
- package/dist/components/notificationCenter.d.ts +65 -0
- package/dist/components/notificationCenter.js +110 -0
- package/dist/components/progress/Steps.svelte +269 -5
- package/dist/components/progress/Steps.svelte.d.ts +16 -1
- package/dist/components/progress/stepsSummary.d.ts +14 -0
- package/dist/components/progress/stepsSummary.js +32 -0
- package/dist/index.d.ts +48 -1
- package/dist/index.js +39 -1
- package/dist/selection/clickSelection.d.ts +89 -0
- package/dist/selection/clickSelection.js +152 -0
- package/dist/selection/createClickSelection.d.ts +79 -0
- package/dist/selection/createClickSelection.js +134 -0
- package/dist/styles/tokens.css +80 -1
- package/dist/utils/pinInput.d.ts +33 -0
- package/dist/utils/pinInput.js +63 -0
- package/dist/utils/textareaRows.d.ts +23 -0
- package/dist/utils/textareaRows.js +13 -0
- package/dist/utils/themeWipe.d.ts +36 -17
- package/dist/utils/themeWipe.js +145 -35
- package/package.json +3 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
1
2
|
interface Step {
|
|
2
3
|
label?: string;
|
|
3
4
|
description?: string;
|
|
@@ -8,7 +9,21 @@ interface Props {
|
|
|
8
9
|
color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
|
|
9
10
|
size?: 'sm' | 'md' | 'lg';
|
|
10
11
|
class?: string;
|
|
12
|
+
/** Render a summary header button that folds the steps away. */
|
|
13
|
+
collapsible?: boolean;
|
|
14
|
+
/** Folded state (bindable). Only meaningful with `collapsible`. */
|
|
15
|
+
collapsed?: boolean;
|
|
16
|
+
/** Header copy. Default: "N steps left" / "All steps done". */
|
|
17
|
+
summary?: string | Snippet;
|
|
18
|
+
/** Layout. Default 'horizontal'. */
|
|
19
|
+
orientation?: 'horizontal' | 'vertical';
|
|
20
|
+
/** 0–1 progress of the active step → a mini ring on its node. */
|
|
21
|
+
activeProgress?: number;
|
|
22
|
+
/** Hold (ms) after the last step completes before `onFinished`. Default 0. */
|
|
23
|
+
completionDelay?: number;
|
|
24
|
+
/** Fired once after `current` passes the last step (+ `completionDelay`). */
|
|
25
|
+
onFinished?: () => void;
|
|
11
26
|
}
|
|
12
|
-
declare const Steps: import("svelte").Component<Props, {}, "">;
|
|
27
|
+
declare const Steps: import("svelte").Component<Props, {}, "collapsed">;
|
|
13
28
|
type Steps = ReturnType<typeof Steps>;
|
|
14
29
|
export default Steps;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers behind Steps' collapsible summary header (jatui-lltq9).
|
|
3
|
+
*
|
|
4
|
+
* `current` is Steps' 1-based active step: steps before it are complete, the
|
|
5
|
+
* step at it is in progress, and `current > count` means every step is done.
|
|
6
|
+
*/
|
|
7
|
+
/** Steps still to finish, counting the active one. Never negative. */
|
|
8
|
+
export declare function stepsRemaining(count: number, current: number): number;
|
|
9
|
+
/** True once `current` has moved past the last step. */
|
|
10
|
+
export declare function isStepsFinished(count: number, current: number): boolean;
|
|
11
|
+
/** Default header copy: "3 steps left", "1 step left", "All steps done". */
|
|
12
|
+
export declare function stepsSummary(count: number, current: number): string;
|
|
13
|
+
/** Clamp an active-step progress fraction to 0–1 (non-finite → 0). */
|
|
14
|
+
export declare function clampStepProgress(value: number): number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers behind Steps' collapsible summary header (jatui-lltq9).
|
|
3
|
+
*
|
|
4
|
+
* `current` is Steps' 1-based active step: steps before it are complete, the
|
|
5
|
+
* step at it is in progress, and `current > count` means every step is done.
|
|
6
|
+
*/
|
|
7
|
+
/** Steps still to finish, counting the active one. Never negative. */
|
|
8
|
+
export function stepsRemaining(count, current) {
|
|
9
|
+
if (!Number.isFinite(count) || count <= 0)
|
|
10
|
+
return 0;
|
|
11
|
+
const done = Math.max(0, Math.min(count, Math.floor(current) - 1));
|
|
12
|
+
return count - done;
|
|
13
|
+
}
|
|
14
|
+
/** True once `current` has moved past the last step. */
|
|
15
|
+
export function isStepsFinished(count, current) {
|
|
16
|
+
return count > 0 && current > count;
|
|
17
|
+
}
|
|
18
|
+
/** Default header copy: "3 steps left", "1 step left", "All steps done". */
|
|
19
|
+
export function stepsSummary(count, current) {
|
|
20
|
+
if (!Number.isFinite(count) || count <= 0)
|
|
21
|
+
return 'No steps';
|
|
22
|
+
const left = stepsRemaining(count, current);
|
|
23
|
+
if (left === 0)
|
|
24
|
+
return 'All steps done';
|
|
25
|
+
return `${left} ${left === 1 ? 'step' : 'steps'} left`;
|
|
26
|
+
}
|
|
27
|
+
/** Clamp an active-step progress fraction to 0–1 (non-finite → 0). */
|
|
28
|
+
export function clampStepProgress(value) {
|
|
29
|
+
if (!Number.isFinite(value))
|
|
30
|
+
return 0;
|
|
31
|
+
return value < 0 ? 0 : value > 1 ? 1 : value;
|
|
32
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -57,6 +57,10 @@ export { default as InlineEdit } from './components/InlineEdit.svelte';
|
|
|
57
57
|
export { default as FloatingActionBar } from './components/FloatingActionBar.svelte';
|
|
58
58
|
export { default as BulkActionBar } from './components/BulkActionBar.svelte';
|
|
59
59
|
export type { BulkAction, BulkActionGroup } from './components/BulkActionBar.svelte';
|
|
60
|
+
export { createClickSelection } from './selection/createClickSelection';
|
|
61
|
+
export type { ClickSelection, ClickSelectionOptions, ClickSelectionKeyOptions } from './selection/createClickSelection';
|
|
62
|
+
export { applyClick as applySelectionClick, extendTo as extendSelectionTo, rangeBetween as selectionRange, selectionKey, isAdditive as isAdditiveClick } from './selection/clickSelection';
|
|
63
|
+
export type { SelectionModifiers, SelectionState, SelectionKeyEvent, SelectionKeyAction, ArrowKey as SelectionArrowKey } from './selection/clickSelection';
|
|
60
64
|
export { default as HunkDiffView } from './components/HunkDiffView.svelte';
|
|
61
65
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
62
66
|
export { default as PresenceStack } from './components/PresenceStack.svelte';
|
|
@@ -71,6 +75,10 @@ export { default as InfiniteSearchGrid } from './components/InfiniteSearchGrid.s
|
|
|
71
75
|
export { createInfiniteLoader } from './components/infiniteGridLoader';
|
|
72
76
|
export type { InfinitePage, InfiniteLoadContext, InfiniteLoadFn, InfiniteLoader, InfiniteLoaderState, InfiniteStatus } from './components/infiniteGridLoader';
|
|
73
77
|
export { default as Button } from './components/Button.svelte';
|
|
78
|
+
export { default as IconButton } from './components/IconButton.svelte';
|
|
79
|
+
export type { IconButtonVariant, IconButtonSize } from './components/IconButton.svelte';
|
|
80
|
+
export { default as CloseButton } from './components/CloseButton.svelte';
|
|
81
|
+
export { default as LinkButton } from './components/LinkButton.svelte';
|
|
74
82
|
export { default as Badge } from './components/Badge.svelte';
|
|
75
83
|
export { default as Card } from './components/Card.svelte';
|
|
76
84
|
export { default as Drawer } from './components/Drawer.svelte';
|
|
@@ -79,6 +87,8 @@ export { default as LoadingSpinner } from './components/LoadingSpinner.svelte';
|
|
|
79
87
|
export { default as StatusBadge } from './components/StatusBadge.svelte';
|
|
80
88
|
export { default as TextInput } from './components/TextInput.svelte';
|
|
81
89
|
export { default as TextArea } from './components/TextArea.svelte';
|
|
90
|
+
export { textareaRowsHeight } from './utils/textareaRows';
|
|
91
|
+
export type { TextareaRowsInput } from './utils/textareaRows';
|
|
82
92
|
export { default as SelectInput } from './components/SelectInput.svelte';
|
|
83
93
|
export { default as PhoneInput } from './components/PhoneInput.svelte';
|
|
84
94
|
export { default as AudioWaveform } from './components/AudioWaveform.svelte';
|
|
@@ -198,6 +208,8 @@ export type { MapJob, MapAgent } from './types/maps';
|
|
|
198
208
|
export { loadGoogleMapsAPI, isGoogleMapsLoaded, isPlacesLoaded, isGeometryLoaded, } from './utils/googleMapsLoader';
|
|
199
209
|
export { tooltip } from './actions/tooltip';
|
|
200
210
|
export type { TooltipOptions } from './actions/tooltip';
|
|
211
|
+
export { scrollEdge, edgeOpacity, edgeDistances } from './actions/scrollEdge';
|
|
212
|
+
export type { ScrollEdgeOptions, ScrollEdgeSide } from './actions/scrollEdge';
|
|
201
213
|
export { staggerEntrance } from './actions/staggerEntrance';
|
|
202
214
|
export type { StaggerEntranceOptions } from './actions/staggerEntrance';
|
|
203
215
|
export { attention } from './actions/attention';
|
|
@@ -205,12 +217,15 @@ export type { AttentionEffect, AttentionOptions } from './actions/attention';
|
|
|
205
217
|
export { reveal, REVEAL_HIDDEN_CLASS, REVEAL_BASE_CLASS, REVEAL_OFF_SELECTOR, REVEAL_MAX_DELAY_SECONDS } from './actions/reveal';
|
|
206
218
|
export type { RevealOptions, RevealVariant } from './actions/reveal';
|
|
207
219
|
export { default as ThumbnailGrid } from './components/ThumbnailGrid.svelte';
|
|
220
|
+
export { default as MediaExplorer } from "./components/MediaExplorer.svelte";
|
|
221
|
+
export type { ExplorerSource } from "./components/MediaExplorer.svelte";
|
|
208
222
|
export type { ThumbnailItem } from './components/ThumbnailGrid.svelte';
|
|
209
223
|
export { floating, makeVirtualAnchor } from './actions/floating';
|
|
210
224
|
export type { FloatingOptions, Placement } from './actions/floating';
|
|
211
225
|
export { glide } from './transitions/glide';
|
|
212
226
|
export type { GlideOrigin, GlideParams } from './transitions/glide';
|
|
213
|
-
export { themeWipe } from './utils/themeWipe';
|
|
227
|
+
export { themeWipe, wipeOrigin, coverRadius } from './utils/themeWipe';
|
|
228
|
+
export type { ThemeWipeOptions } from './utils/themeWipe';
|
|
214
229
|
export { viewTransition } from './utils/viewTransition';
|
|
215
230
|
export { MOTION_LEVELS, DEFAULT_MOTION_LEVEL, MOTION_LEVEL_ATTR, MOTION_DURATIONS, MOTION_LEVEL_SCALE, MOTION_LEVEL_TRAVEL, MOTION_EASING_POINTS, isMotionLevel, parseMotionLevel, prefersReducedMotion, resolveMotionLevel, motionContextFor, getMotionContext, motionDuration, effectiveEasing, setMotionLevel, cubicBezier, motionEasing, motionFade, motionScale, motionSlide, resolveMotionTiming, createMotionLevelStore } from './motion';
|
|
216
231
|
export type { MotionLevel, MotionDurationToken, MotionEasingToken, MotionContext, EasingFn, MotionTransitionParams, MotionTransitionConfig, MotionFadeParams, MotionScaleParams, MotionSlideParams, ResolvedMotionTiming, MotionState, MotionLevelStore, MotionLevelStoreOptions } from './motion';
|
|
@@ -234,6 +249,7 @@ export { default as AnimatedNumber } from './components/AnimatedNumber.svelte';
|
|
|
234
249
|
export { default as CopyButton } from './components/CopyButton.svelte';
|
|
235
250
|
export { default as HoldToConfirm } from './components/HoldToConfirm.svelte';
|
|
236
251
|
export { default as PinInput } from './components/PinInput.svelte';
|
|
252
|
+
export { applyCellInput as applyPinCellInput, distribute as distributePin, cellsFromValue as pinCellsFromValue, isPinComplete, sanitizePin } from './utils/pinInput';
|
|
237
253
|
export { default as PasswordMeter } from './components/PasswordMeter.svelte';
|
|
238
254
|
export { default as QuantityStepper } from './components/QuantityStepper.svelte';
|
|
239
255
|
export { default as TypingIndicator } from './components/messaging/TypingIndicator.svelte';
|
|
@@ -255,6 +271,9 @@ export type { StickState, StickToBottomOptions, StickController } from './action
|
|
|
255
271
|
export { ansiToHtml, ansiToHtmlStateful, ansiToHtmlWithLinks, ansiToHtmlWithHierarchy, linkifyUrls, stripAnsi, classifyLine, parseOutputLines, createDefaultAnsiState } from './utils/ansiToHtml';
|
|
256
272
|
export type { AnsiState, ParsedLine } from './utils/ansiToHtml';
|
|
257
273
|
export { default as Breadcrumb } from './components/basic/Breadcrumb.svelte';
|
|
274
|
+
export type { BreadcrumbItem } from './components/basic/Breadcrumb.svelte';
|
|
275
|
+
export { collapseBreadcrumb } from './components/basic/breadcrumbCollapse';
|
|
276
|
+
export type { BreadcrumbSlot } from './components/basic/breadcrumbCollapse';
|
|
258
277
|
export { default as Alert } from './components/basic/Alert.svelte';
|
|
259
278
|
export { default as ButtonGroup } from './components/basic/ButtonGroup.svelte';
|
|
260
279
|
export { default as Switch } from './components/basic/Switch.svelte';
|
|
@@ -304,3 +323,31 @@ export { mergeReviewCounts, summarizeReview, reviewSentence, buildReviewFilters,
|
|
|
304
323
|
export type { ReviewTone, ReviewVerdictDef, ReviewCounts, ReviewRound, ReviewFilterChip, ReviewSummary, BuildFiltersInput } from './components/review/reviewSummary';
|
|
305
324
|
export { MEDIA_ZOOM_KEYS, mediaZoomHint } from './components/review/mediaZoom';
|
|
306
325
|
export type { MediaZoomState, MediaZoomView, MediaZoomFooterContext, MediaZoomAction, MediaZoomKeyDef, MediaZoomKeyContext } from './components/review/mediaZoom';
|
|
326
|
+
export { default as ShimmerText } from './components/agent/ShimmerText.svelte';
|
|
327
|
+
export { default as AgentThinking } from './components/agent/AgentThinking.svelte';
|
|
328
|
+
export { formatThinkingTime } from './components/agent/agentThinking';
|
|
329
|
+
export type { AgentThinkingVariant, AgentThinkingTone } from './components/agent/agentThinking';
|
|
330
|
+
export { default as AgentLog } from './components/agent/AgentLog.svelte';
|
|
331
|
+
export { default as AgentLogRow } from './components/agent/AgentLogRow.svelte';
|
|
332
|
+
export { createRevealTicker } from './components/agent/revealTicker';
|
|
333
|
+
export type { RevealTickerOptions, RevealTicker } from './components/agent/revealTicker';
|
|
334
|
+
export { RevealCore, revealDelay, DEFAULT_STEP_INTERVAL as REVEAL_STEP_INTERVAL, DEFAULT_START_DELAY as REVEAL_START_DELAY } from './components/agent/revealCore';
|
|
335
|
+
export type { RevealCoreOptions, RevealClock, RevealCoreHooks } from './components/agent/revealCore';
|
|
336
|
+
export { BRANCH_Y as AGENT_LOG_BRANCH_Y } from './components/agent/agentLog';
|
|
337
|
+
export { default as AgentTaskList } from './components/agent/AgentTaskList.svelte';
|
|
338
|
+
export { countUnits as countAgentTaskUnits, layoutTaskReveal, displayStatus as agentTaskDisplayStatus, isListComplete as isAgentTaskListComplete, foldedByPolicy as agentTaskFoldedByPolicy, sourceStack as agentSourceStack, sourceDomain as agentSourceDomain, sourceLabel as agentSourceLabel } from './components/agent/agentTaskList';
|
|
339
|
+
export type { AgentTask, AgentTaskStep, AgentTaskChip, AgentTaskSource, AgentTaskStatus, CollapsePolicy as AgentTaskCollapsePolicy, TaskReveal } from './components/agent/agentTaskList';
|
|
340
|
+
export { default as Questionnaire } from './components/agent/Questionnaire.svelte';
|
|
341
|
+
export { emptyAnswers as emptyQuestionnaireAnswers, normalizeAnswers as normalizeQuestionnaireAnswers, finalizeAnswers as finalizeQuestionnaireAnswers, isComplete as isQuestionnaireComplete, isAnswered as isQuestionAnswered, digitToRow as questionnaireDigitToRow } from './components/agent/questionnaire';
|
|
342
|
+
export type { QuestionnaireQuestion, QuestionnaireOption, QuestionnaireAnswer, QuestionnaireAnswers } from './components/agent/questionnaire';
|
|
343
|
+
export { default as ContextMeter } from './components/agent/ContextMeter.svelte';
|
|
344
|
+
export { computeContextMeter, buildBreakdown as buildContextBreakdown, formatCompact as formatCompactTokens, formatPercent as formatContextPercent, segmentColor as contextSegmentColor, meterValueText as contextMeterValueText } from './components/agent/contextMeter';
|
|
345
|
+
export type { ContextSegment, ContextGroup, ContextTone, ContextMeterModel, MeterSegment as ContextMeterSegment, BreakdownRow as ContextBreakdownRow } from './components/agent/contextMeter';
|
|
346
|
+
export { default as ComposerGlow } from './components/agent/ComposerGlow.svelte';
|
|
347
|
+
export { default as StreamedText } from './components/agent/StreamedText.svelte';
|
|
348
|
+
export { splitStreamedText } from './components/agent/streamedText';
|
|
349
|
+
export type { StreamedBlock, StreamedTextSplit } from './components/agent/streamedText';
|
|
350
|
+
export { stepsSummary, stepsRemaining, isStepsFinished, clampStepProgress } from './components/progress/stepsSummary';
|
|
351
|
+
export { default as NotificationCenter } from './components/NotificationCenter.svelte';
|
|
352
|
+
export { DEFAULT_NOTIFICATION_TABS, DATE_GROUP_LABELS as NOTIFICATION_DATE_GROUP_LABELS, dateGroupKey as notificationDateGroupKey, groupNotificationsByDate, toTimestampMs as notificationTimestampMs, isNotificationUnread, unreadIds as unreadNotificationIds, filterByTab as filterNotificationsByTab, tabCounts as notificationTabCounts, unreadSummary as notificationUnreadSummary } from './components/notificationCenter';
|
|
353
|
+
export type { NotificationItem, NotificationAction, NotificationTab, NotificationStatus, NotificationGroup, DateGroupKey as NotificationDateGroupKey } from './components/notificationCenter';
|
package/dist/index.js
CHANGED
|
@@ -93,6 +93,12 @@ export { default as SortDropdown } from './components/SortDropdown.svelte';
|
|
|
93
93
|
export { default as InlineEdit } from './components/InlineEdit.svelte';
|
|
94
94
|
export { default as FloatingActionBar } from './components/FloatingActionBar.svelte';
|
|
95
95
|
export { default as BulkActionBar } from './components/BulkActionBar.svelte';
|
|
96
|
+
// Click selection (qimg-x9ifp.1): the pointer-first multi-selection that feeds
|
|
97
|
+
// BulkActionBar — click / Ctrl-⌘ click / Shift click ranges, Shift+arrows,
|
|
98
|
+
// Ctrl/⌘ A, Esc. Pure rules in selection/clickSelection.ts (unit-tested), a
|
|
99
|
+
// reactive SvelteSet controller in createClickSelection.svelte.ts.
|
|
100
|
+
export { createClickSelection } from './selection/createClickSelection';
|
|
101
|
+
export { applyClick as applySelectionClick, extendTo as extendSelectionTo, rangeBetween as selectionRange, selectionKey, isAdditive as isAdditiveClick } from './selection/clickSelection';
|
|
96
102
|
export { default as HunkDiffView } from './components/HunkDiffView.svelte';
|
|
97
103
|
// Primitives — accessibility + form-editor scaffolding
|
|
98
104
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
@@ -106,6 +112,10 @@ export { default as InfiniteSearchGrid } from './components/InfiniteSearchGrid.s
|
|
|
106
112
|
export { createInfiniteLoader } from './components/infiniteGridLoader';
|
|
107
113
|
// Components — from Flush
|
|
108
114
|
export { default as Button } from './components/Button.svelte';
|
|
115
|
+
// Icon-only / close / text-link buttons (BoardUI wave 1, jatui-owg2z)
|
|
116
|
+
export { default as IconButton } from './components/IconButton.svelte';
|
|
117
|
+
export { default as CloseButton } from './components/CloseButton.svelte';
|
|
118
|
+
export { default as LinkButton } from './components/LinkButton.svelte';
|
|
109
119
|
export { default as Badge } from './components/Badge.svelte';
|
|
110
120
|
export { default as Card } from './components/Card.svelte';
|
|
111
121
|
export { default as Drawer } from './components/Drawer.svelte';
|
|
@@ -114,6 +124,7 @@ export { default as LoadingSpinner } from './components/LoadingSpinner.svelte';
|
|
|
114
124
|
export { default as StatusBadge } from './components/StatusBadge.svelte';
|
|
115
125
|
export { default as TextInput } from './components/TextInput.svelte';
|
|
116
126
|
export { default as TextArea } from './components/TextArea.svelte';
|
|
127
|
+
export { textareaRowsHeight } from './utils/textareaRows';
|
|
117
128
|
export { default as SelectInput } from './components/SelectInput.svelte';
|
|
118
129
|
export { default as PhoneInput } from './components/PhoneInput.svelte';
|
|
119
130
|
// Components — from Chimaro
|
|
@@ -239,6 +250,8 @@ export { loadGoogleMapsAPI, isGoogleMapsLoaded, isPlacesLoaded, isGeometryLoaded
|
|
|
239
250
|
// ============================================================
|
|
240
251
|
// Motion primitives
|
|
241
252
|
export { tooltip } from './actions/tooltip';
|
|
253
|
+
// Progressive-blur scroll-region edge (BoardUI wave 1, jatui-owg2z)
|
|
254
|
+
export { scrollEdge, edgeOpacity, edgeDistances } from './actions/scrollEdge';
|
|
242
255
|
export { staggerEntrance } from './actions/staggerEntrance';
|
|
243
256
|
export { attention } from './actions/attention';
|
|
244
257
|
// Scroll-reveal (jatui-m4uz3.3, chimaro port): IntersectionObserver
|
|
@@ -248,10 +261,11 @@ export { reveal, REVEAL_HIDDEN_CLASS, REVEAL_BASE_CLASS, REVEAL_OFF_SELECTOR, RE
|
|
|
248
261
|
// Thumbnail grid (jatui-m4uz3.3, chimaro port): generic {id,url,alt} strip
|
|
249
262
|
// with overflow counter, remove/add affordances and a cell snippet override.
|
|
250
263
|
export { default as ThumbnailGrid } from './components/ThumbnailGrid.svelte';
|
|
264
|
+
export { default as MediaExplorer } from "./components/MediaExplorer.svelte";
|
|
251
265
|
export { floating, makeVirtualAnchor } from './actions/floating';
|
|
252
266
|
export { glide } from './transitions/glide';
|
|
253
267
|
// View Transitions kit
|
|
254
|
-
export { themeWipe } from './utils/themeWipe';
|
|
268
|
+
export { themeWipe, wipeOrigin, coverRadius } from './utils/themeWipe';
|
|
255
269
|
export { viewTransition } from './utils/viewTransition';
|
|
256
270
|
// Motion level kit (jatui-j6833): data-motion="calm|standard|lively" scales
|
|
257
271
|
// the --motion-* tokens per surface (styles/motion.css); these helpers,
|
|
@@ -285,6 +299,7 @@ export { default as CopyButton } from './components/CopyButton.svelte';
|
|
|
285
299
|
export { default as HoldToConfirm } from './components/HoldToConfirm.svelte';
|
|
286
300
|
// Form gaps
|
|
287
301
|
export { default as PinInput } from './components/PinInput.svelte';
|
|
302
|
+
export { applyCellInput as applyPinCellInput, distribute as distributePin, cellsFromValue as pinCellsFromValue, isPinComplete, sanitizePin } from './utils/pinInput';
|
|
288
303
|
export { default as PasswordMeter } from './components/PasswordMeter.svelte';
|
|
289
304
|
export { default as QuantityStepper } from './components/QuantityStepper.svelte';
|
|
290
305
|
// Messaging
|
|
@@ -314,6 +329,7 @@ export { createStickToBottom } from './actions/stickToBottom';
|
|
|
314
329
|
// ANSI terminal output → HTML (upstreamed from the JAT IDE, U5 — jatui-rl7w6.3).
|
|
315
330
|
export { ansiToHtml, ansiToHtmlStateful, ansiToHtmlWithLinks, ansiToHtmlWithHierarchy, linkifyUrls, stripAnsi, classifyLine, parseOutputLines, createDefaultAnsiState } from './utils/ansiToHtml';
|
|
316
331
|
export { default as Breadcrumb } from './components/basic/Breadcrumb.svelte';
|
|
332
|
+
export { collapseBreadcrumb } from './components/basic/breadcrumbCollapse';
|
|
317
333
|
export { default as Alert } from './components/basic/Alert.svelte';
|
|
318
334
|
export { default as ButtonGroup } from './components/basic/ButtonGroup.svelte';
|
|
319
335
|
export { default as Switch } from './components/basic/Switch.svelte';
|
|
@@ -355,3 +371,25 @@ export { default as MediaZoomLightbox } from './components/review/MediaZoomLight
|
|
|
355
371
|
export { reviewKeys, createReviewKeys, reviewKeyRows, resolveReviewKey, REVIEW_KEY_TABLE } from './actions/reviewKeys';
|
|
356
372
|
export { mergeReviewCounts, summarizeReview, reviewSentence, buildReviewFilters, orderRounds, formatRoundDate } from './components/review/reviewSummary';
|
|
357
373
|
export { MEDIA_ZOOM_KEYS, mediaZoomHint } from './components/review/mediaZoom';
|
|
374
|
+
// Agent surfaces (BoardUI wave 2 — jatui-lltq9). Free BoardUI items are MIT
|
|
375
|
+
// (THIRD_PARTY_NOTICES.md); Task List / Questionnaire / ContextMeter are clean-room.
|
|
376
|
+
export { default as ShimmerText } from './components/agent/ShimmerText.svelte';
|
|
377
|
+
export { default as AgentThinking } from './components/agent/AgentThinking.svelte';
|
|
378
|
+
export { formatThinkingTime } from './components/agent/agentThinking';
|
|
379
|
+
export { default as AgentLog } from './components/agent/AgentLog.svelte';
|
|
380
|
+
export { default as AgentLogRow } from './components/agent/AgentLogRow.svelte';
|
|
381
|
+
export { createRevealTicker } from './components/agent/revealTicker';
|
|
382
|
+
export { RevealCore, revealDelay, DEFAULT_STEP_INTERVAL as REVEAL_STEP_INTERVAL, DEFAULT_START_DELAY as REVEAL_START_DELAY } from './components/agent/revealCore';
|
|
383
|
+
export { BRANCH_Y as AGENT_LOG_BRANCH_Y } from './components/agent/agentLog';
|
|
384
|
+
export { default as AgentTaskList } from './components/agent/AgentTaskList.svelte';
|
|
385
|
+
export { countUnits as countAgentTaskUnits, layoutTaskReveal, displayStatus as agentTaskDisplayStatus, isListComplete as isAgentTaskListComplete, foldedByPolicy as agentTaskFoldedByPolicy, sourceStack as agentSourceStack, sourceDomain as agentSourceDomain, sourceLabel as agentSourceLabel } from './components/agent/agentTaskList';
|
|
386
|
+
export { default as Questionnaire } from './components/agent/Questionnaire.svelte';
|
|
387
|
+
export { emptyAnswers as emptyQuestionnaireAnswers, normalizeAnswers as normalizeQuestionnaireAnswers, finalizeAnswers as finalizeQuestionnaireAnswers, isComplete as isQuestionnaireComplete, isAnswered as isQuestionAnswered, digitToRow as questionnaireDigitToRow } from './components/agent/questionnaire';
|
|
388
|
+
export { default as ContextMeter } from './components/agent/ContextMeter.svelte';
|
|
389
|
+
export { computeContextMeter, buildBreakdown as buildContextBreakdown, formatCompact as formatCompactTokens, formatPercent as formatContextPercent, segmentColor as contextSegmentColor, meterValueText as contextMeterValueText } from './components/agent/contextMeter';
|
|
390
|
+
export { default as ComposerGlow } from './components/agent/ComposerGlow.svelte';
|
|
391
|
+
export { default as StreamedText } from './components/agent/StreamedText.svelte';
|
|
392
|
+
export { splitStreamedText } from './components/agent/streamedText';
|
|
393
|
+
export { stepsSummary, stepsRemaining, isStepsFinished, clampStepProgress } from './components/progress/stepsSummary';
|
|
394
|
+
export { default as NotificationCenter } from './components/NotificationCenter.svelte';
|
|
395
|
+
export { DEFAULT_NOTIFICATION_TABS, DATE_GROUP_LABELS as NOTIFICATION_DATE_GROUP_LABELS, dateGroupKey as notificationDateGroupKey, groupNotificationsByDate, toTimestampMs as notificationTimestampMs, isNotificationUnread, unreadIds as unreadNotificationIds, filterByTab as filterNotificationsByTab, tabCounts as notificationTabCounts, unreadSummary as notificationUnreadSummary } from './components/notificationCenter';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pointer-first multi-selection: the desktop-standard click rules, as pure
|
|
3
|
+
* functions over a plain `Set` (a `SvelteSet` is a `Set`, so the reactive
|
|
4
|
+
* controller in `createClickSelection.svelte.ts` runs exactly this code).
|
|
5
|
+
*
|
|
6
|
+
* click select only this one; it becomes the anchor
|
|
7
|
+
* Ctrl/⌘ click toggle this one; it becomes the anchor
|
|
8
|
+
* Shift click select the range anchor..this, in the current id order
|
|
9
|
+
* (replaces the selection; the anchor stays put)
|
|
10
|
+
* Shift Ctrl/⌘ add the range anchor..this to the selection
|
|
11
|
+
* Shift arrows extend from the anchor (the caller says which id an
|
|
12
|
+
* arrow lands on, because only it knows the layout)
|
|
13
|
+
* Ctrl/⌘ A select every id
|
|
14
|
+
* Esc clear
|
|
15
|
+
*
|
|
16
|
+
* The selection is a set of ids, not of positions: re-sorting, filtering or
|
|
17
|
+
* live inserts keep it, and an id that is no longer in `ids` is simply not
|
|
18
|
+
* selected any more (`prune` drops it, the readers filter it out first).
|
|
19
|
+
*
|
|
20
|
+
* `head` is the moving end of the last range (the last id clicked or
|
|
21
|
+
* extended to), so Shift+arrows keep growing from where they left off.
|
|
22
|
+
*
|
|
23
|
+
* No DOM, no Svelte: unit-tested in tests/unit/clickSelection.test.ts.
|
|
24
|
+
*/
|
|
25
|
+
/** The modifier keys of a click (a MouseEvent or KeyboardEvent satisfies it). */
|
|
26
|
+
export interface SelectionModifiers {
|
|
27
|
+
shiftKey?: boolean;
|
|
28
|
+
ctrlKey?: boolean;
|
|
29
|
+
metaKey?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** What a selection is: the set, plus the fixed and the moving end of a range. */
|
|
32
|
+
export interface SelectionState<Id = string> {
|
|
33
|
+
selected: Set<Id>;
|
|
34
|
+
anchor: Id | null;
|
|
35
|
+
head: Id | null;
|
|
36
|
+
}
|
|
37
|
+
/** Ctrl on Linux/Windows, ⌘ on macOS: either counts as "toggle / add". */
|
|
38
|
+
export declare const isAdditive: (m: SelectionModifiers) => boolean;
|
|
39
|
+
/** The ids from `a` to `b` inclusive, in `ids` order (either direction). Empty when either is missing. */
|
|
40
|
+
export declare function rangeBetween<Id>(ids: readonly Id[], a: Id, b: Id): Id[];
|
|
41
|
+
/** Select only `id` and make it the anchor (a plain click, or a plain arrow move). */
|
|
42
|
+
export declare function selectOnly<Id>(s: SelectionState<Id>, id: Id): void;
|
|
43
|
+
/** Toggle `id` and make it the anchor (Ctrl/⌘ click). */
|
|
44
|
+
export declare function toggleOne<Id>(s: SelectionState<Id>, id: Id): void;
|
|
45
|
+
/**
|
|
46
|
+
* Select the range from the anchor to `id` (Shift click / Shift arrows).
|
|
47
|
+
* `additive` keeps what was selected and adds the range (Shift+Ctrl/⌘).
|
|
48
|
+
* With no usable anchor (none yet, or it vanished) it behaves as a plain
|
|
49
|
+
* click on `id` (or a toggle-on when additive).
|
|
50
|
+
*/
|
|
51
|
+
export declare function extendTo<Id>(s: SelectionState<Id>, ids: readonly Id[], id: Id, additive?: boolean): void;
|
|
52
|
+
/** Apply one click on `id` with its modifiers. Ids not in `ids` are ignored. */
|
|
53
|
+
export declare function applyClick<Id>(s: SelectionState<Id>, ids: readonly Id[], id: Id, m?: SelectionModifiers): void;
|
|
54
|
+
/** Select every id (Ctrl/⌘ A). The anchor stays if it is still there, else the first id. */
|
|
55
|
+
export declare function selectAll<Id>(s: SelectionState<Id>, ids: readonly Id[]): void;
|
|
56
|
+
/** Clear the selection and the range ends (Esc). */
|
|
57
|
+
export declare function clearSelection<Id>(s: SelectionState<Id>): void;
|
|
58
|
+
/** Drop selected ids that are no longer in `ids` (and an anchor or head that vanished). True when anything changed. */
|
|
59
|
+
export declare function prune<Id>(s: SelectionState<Id>, ids: readonly Id[]): boolean;
|
|
60
|
+
/** The selected ids that are still present, in `ids` order (what a range or a list shows). */
|
|
61
|
+
export declare const inOrder: <Id>(selected: ReadonlySet<Id>, ids: readonly Id[]) => Id[];
|
|
62
|
+
/** The selected ids that are still present, in the order they were selected (Set insertion order). */
|
|
63
|
+
export declare function inPickOrder<Id>(selected: ReadonlySet<Id>, ids: readonly Id[]): Id[];
|
|
64
|
+
/** The keyboard part of a keydown (a KeyboardEvent satisfies it). */
|
|
65
|
+
export interface SelectionKeyEvent extends SelectionModifiers {
|
|
66
|
+
key: string;
|
|
67
|
+
altKey?: boolean;
|
|
68
|
+
target?: EventTarget | null;
|
|
69
|
+
defaultPrevented?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export declare const ARROW_KEYS: readonly ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
|
|
72
|
+
export type ArrowKey = (typeof ARROW_KEYS)[number];
|
|
73
|
+
/** What a selection key does, decided without touching anything. */
|
|
74
|
+
export type SelectionKeyAction = {
|
|
75
|
+
kind: 'selectAll';
|
|
76
|
+
} | {
|
|
77
|
+
kind: 'clear';
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'extend';
|
|
80
|
+
key: ArrowKey;
|
|
81
|
+
additive: boolean;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Which selection key this is, if any: Ctrl/⌘ A (select all), Esc (clear,
|
|
85
|
+
* only while something is selected, so Esc keeps its other meanings when
|
|
86
|
+
* nothing is), Shift+arrows (extend; with Ctrl/⌘ too, add the range).
|
|
87
|
+
* Never while typing in a field, never with Alt, never when already handled.
|
|
88
|
+
*/
|
|
89
|
+
export declare function selectionKey(e: SelectionKeyEvent, hasSelection: boolean): SelectionKeyAction | null;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pointer-first multi-selection: the desktop-standard click rules, as pure
|
|
3
|
+
* functions over a plain `Set` (a `SvelteSet` is a `Set`, so the reactive
|
|
4
|
+
* controller in `createClickSelection.svelte.ts` runs exactly this code).
|
|
5
|
+
*
|
|
6
|
+
* click select only this one; it becomes the anchor
|
|
7
|
+
* Ctrl/⌘ click toggle this one; it becomes the anchor
|
|
8
|
+
* Shift click select the range anchor..this, in the current id order
|
|
9
|
+
* (replaces the selection; the anchor stays put)
|
|
10
|
+
* Shift Ctrl/⌘ add the range anchor..this to the selection
|
|
11
|
+
* Shift arrows extend from the anchor (the caller says which id an
|
|
12
|
+
* arrow lands on, because only it knows the layout)
|
|
13
|
+
* Ctrl/⌘ A select every id
|
|
14
|
+
* Esc clear
|
|
15
|
+
*
|
|
16
|
+
* The selection is a set of ids, not of positions: re-sorting, filtering or
|
|
17
|
+
* live inserts keep it, and an id that is no longer in `ids` is simply not
|
|
18
|
+
* selected any more (`prune` drops it, the readers filter it out first).
|
|
19
|
+
*
|
|
20
|
+
* `head` is the moving end of the last range (the last id clicked or
|
|
21
|
+
* extended to), so Shift+arrows keep growing from where they left off.
|
|
22
|
+
*
|
|
23
|
+
* No DOM, no Svelte: unit-tested in tests/unit/clickSelection.test.ts.
|
|
24
|
+
*/
|
|
25
|
+
import { isTypingTarget } from '../utils/keyboardShortcutRegistry';
|
|
26
|
+
/** Ctrl on Linux/Windows, ⌘ on macOS: either counts as "toggle / add". */
|
|
27
|
+
export const isAdditive = (m) => !!(m.ctrlKey || m.metaKey);
|
|
28
|
+
/** The ids from `a` to `b` inclusive, in `ids` order (either direction). Empty when either is missing. */
|
|
29
|
+
export function rangeBetween(ids, a, b) {
|
|
30
|
+
const i = ids.indexOf(a);
|
|
31
|
+
const j = ids.indexOf(b);
|
|
32
|
+
if (i < 0 || j < 0)
|
|
33
|
+
return [];
|
|
34
|
+
return i <= j ? ids.slice(i, j + 1) : ids.slice(j, i + 1);
|
|
35
|
+
}
|
|
36
|
+
/** Select only `id` and make it the anchor (a plain click, or a plain arrow move). */
|
|
37
|
+
export function selectOnly(s, id) {
|
|
38
|
+
s.selected.clear();
|
|
39
|
+
s.selected.add(id);
|
|
40
|
+
s.anchor = id;
|
|
41
|
+
s.head = id;
|
|
42
|
+
}
|
|
43
|
+
/** Toggle `id` and make it the anchor (Ctrl/⌘ click). */
|
|
44
|
+
export function toggleOne(s, id) {
|
|
45
|
+
if (s.selected.has(id))
|
|
46
|
+
s.selected.delete(id);
|
|
47
|
+
else
|
|
48
|
+
s.selected.add(id);
|
|
49
|
+
s.anchor = id;
|
|
50
|
+
s.head = id;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Select the range from the anchor to `id` (Shift click / Shift arrows).
|
|
54
|
+
* `additive` keeps what was selected and adds the range (Shift+Ctrl/⌘).
|
|
55
|
+
* With no usable anchor (none yet, or it vanished) it behaves as a plain
|
|
56
|
+
* click on `id` (or a toggle-on when additive).
|
|
57
|
+
*/
|
|
58
|
+
export function extendTo(s, ids, id, additive = false) {
|
|
59
|
+
if (!ids.includes(id))
|
|
60
|
+
return;
|
|
61
|
+
const anchor = s.anchor !== null && ids.includes(s.anchor) ? s.anchor : null;
|
|
62
|
+
if (anchor === null) {
|
|
63
|
+
if (additive) {
|
|
64
|
+
s.selected.add(id);
|
|
65
|
+
s.anchor = id;
|
|
66
|
+
s.head = id;
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
selectOnly(s, id);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const range = rangeBetween(ids, anchor, id);
|
|
73
|
+
if (!additive)
|
|
74
|
+
s.selected.clear();
|
|
75
|
+
for (const x of range)
|
|
76
|
+
s.selected.add(x);
|
|
77
|
+
s.head = id;
|
|
78
|
+
}
|
|
79
|
+
/** Apply one click on `id` with its modifiers. Ids not in `ids` are ignored. */
|
|
80
|
+
export function applyClick(s, ids, id, m = {}) {
|
|
81
|
+
if (!ids.includes(id))
|
|
82
|
+
return;
|
|
83
|
+
prune(s, ids);
|
|
84
|
+
if (m.shiftKey)
|
|
85
|
+
return extendTo(s, ids, id, isAdditive(m));
|
|
86
|
+
if (isAdditive(m))
|
|
87
|
+
return toggleOne(s, id);
|
|
88
|
+
selectOnly(s, id);
|
|
89
|
+
}
|
|
90
|
+
/** Select every id (Ctrl/⌘ A). The anchor stays if it is still there, else the first id. */
|
|
91
|
+
export function selectAll(s, ids) {
|
|
92
|
+
s.selected.clear();
|
|
93
|
+
for (const x of ids)
|
|
94
|
+
s.selected.add(x);
|
|
95
|
+
if (s.anchor === null || !ids.includes(s.anchor))
|
|
96
|
+
s.anchor = ids[0] ?? null;
|
|
97
|
+
s.head = ids.at(-1) ?? null;
|
|
98
|
+
}
|
|
99
|
+
/** Clear the selection and the range ends (Esc). */
|
|
100
|
+
export function clearSelection(s) {
|
|
101
|
+
s.selected.clear();
|
|
102
|
+
s.anchor = null;
|
|
103
|
+
s.head = null;
|
|
104
|
+
}
|
|
105
|
+
/** Drop selected ids that are no longer in `ids` (and an anchor or head that vanished). True when anything changed. */
|
|
106
|
+
export function prune(s, ids) {
|
|
107
|
+
const present = new Set(ids);
|
|
108
|
+
let changed = false;
|
|
109
|
+
for (const x of [...s.selected]) {
|
|
110
|
+
if (!present.has(x)) {
|
|
111
|
+
s.selected.delete(x);
|
|
112
|
+
changed = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (s.anchor !== null && !present.has(s.anchor)) {
|
|
116
|
+
s.anchor = null;
|
|
117
|
+
changed = true;
|
|
118
|
+
}
|
|
119
|
+
if (s.head !== null && !present.has(s.head)) {
|
|
120
|
+
s.head = null;
|
|
121
|
+
changed = true;
|
|
122
|
+
}
|
|
123
|
+
return changed;
|
|
124
|
+
}
|
|
125
|
+
/** The selected ids that are still present, in `ids` order (what a range or a list shows). */
|
|
126
|
+
export const inOrder = (selected, ids) => ids.filter((x) => selected.has(x));
|
|
127
|
+
/** The selected ids that are still present, in the order they were selected (Set insertion order). */
|
|
128
|
+
export function inPickOrder(selected, ids) {
|
|
129
|
+
const present = new Set(ids);
|
|
130
|
+
return [...selected].filter((x) => present.has(x));
|
|
131
|
+
}
|
|
132
|
+
export const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
|
|
133
|
+
/**
|
|
134
|
+
* Which selection key this is, if any: Ctrl/⌘ A (select all), Esc (clear,
|
|
135
|
+
* only while something is selected, so Esc keeps its other meanings when
|
|
136
|
+
* nothing is), Shift+arrows (extend; with Ctrl/⌘ too, add the range).
|
|
137
|
+
* Never while typing in a field, never with Alt, never when already handled.
|
|
138
|
+
*/
|
|
139
|
+
export function selectionKey(e, hasSelection) {
|
|
140
|
+
if (e.defaultPrevented || e.altKey)
|
|
141
|
+
return null;
|
|
142
|
+
if (isTypingTarget(e.target ?? null))
|
|
143
|
+
return null;
|
|
144
|
+
const mod = isAdditive(e);
|
|
145
|
+
if (mod && !e.shiftKey && (e.key === 'a' || e.key === 'A'))
|
|
146
|
+
return { kind: 'selectAll' };
|
|
147
|
+
if (e.key === 'Escape' && !mod && !e.shiftKey)
|
|
148
|
+
return hasSelection ? { kind: 'clear' } : null;
|
|
149
|
+
if (e.shiftKey && ARROW_KEYS.includes(e.key))
|
|
150
|
+
return { kind: 'extend', key: e.key, additive: mod };
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createClickSelection: a reactive multi-selection for grids and lists that are
|
|
3
|
+
* used with the mouse (Shift-click ranges, Ctrl/⌘-click toggles) and the
|
|
4
|
+
* keyboard (Shift+arrows, Ctrl/⌘ A, Esc). The rules live in `clickSelection.ts`
|
|
5
|
+
* (pure, unit-tested); this wraps them in a `SvelteSet` plus `$state` ends so a
|
|
6
|
+
* component re-renders when the selection changes.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
*
|
|
10
|
+
* const sel = createClickSelection({ ids: () => items.map((i) => i.id) });
|
|
11
|
+
*
|
|
12
|
+
* {#each items as item (item.id)}
|
|
13
|
+
* <button class:selected={sel.has(item.id)} onclick={(e) => sel.click(item.id, e)}>…</button>
|
|
14
|
+
* {/each}
|
|
15
|
+
*
|
|
16
|
+
* <svelte:window onkeydown={(e) => sel.handleKeydown(e, { next: (from, key) => neighbour(from, key) })} />
|
|
17
|
+
*
|
|
18
|
+
* <BulkActionBar selection={sel.ordered} actions={…} onClear={() => sel.clear()} />
|
|
19
|
+
*
|
|
20
|
+
* `ids()` is read on every call, in display order, so ranges always follow
|
|
21
|
+
* what is on screen; ids that left it are never reported (`ordered`,
|
|
22
|
+
* `picked`, `count`) and are dropped from the set on the next change.
|
|
23
|
+
*
|
|
24
|
+
* With listNav: listNav is the keyboard-only list motion (j/k, vim counts) and
|
|
25
|
+
* has its own opt-in selection (x, V, Shift+J/K). Give a surface ONE selection
|
|
26
|
+
* owner. A pointer-first grid uses clickSelection; a keyboard-first list uses
|
|
27
|
+
* listNav's. To have both, run listNav with `selectable: false` for focus
|
|
28
|
+
* motion and let clickSelection own the set: call `sel.click(id, e)` from the
|
|
29
|
+
* click handler and `sel.extendTo(id)` from your own Shift+J/K.
|
|
30
|
+
*/
|
|
31
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
32
|
+
import { type ArrowKey, type SelectionKeyEvent, type SelectionModifiers } from './clickSelection';
|
|
33
|
+
export interface ClickSelectionOptions<Id = string> {
|
|
34
|
+
/** Every selectable id, in the order shown. Read lazily on each call. */
|
|
35
|
+
ids: () => readonly Id[];
|
|
36
|
+
/** Called after every change, with the selection in display order. */
|
|
37
|
+
onChange?: (ordered: Id[]) => void;
|
|
38
|
+
}
|
|
39
|
+
export interface ClickSelectionKeyOptions<Id = string> {
|
|
40
|
+
/**
|
|
41
|
+
* Where an arrow lands from `from` (null: the selection has no moving end
|
|
42
|
+
* yet). Return null to do nothing (an edge). Only the caller knows the
|
|
43
|
+
* layout (a grid's row above, a list's next item), so it decides.
|
|
44
|
+
*/
|
|
45
|
+
next?: (from: Id | null, key: ArrowKey) => Id | null;
|
|
46
|
+
}
|
|
47
|
+
export interface ClickSelection<Id = string> {
|
|
48
|
+
/** The raw set (may briefly hold an id that just left `ids`; prefer `ordered`). */
|
|
49
|
+
readonly selected: SvelteSet<Id>;
|
|
50
|
+
/** The fixed end of a range: the last plain or Ctrl/⌘ click. */
|
|
51
|
+
readonly anchor: Id | null;
|
|
52
|
+
/** The moving end: the last id clicked or extended to. */
|
|
53
|
+
readonly head: Id | null;
|
|
54
|
+
/** Selected and still present, in display order. */
|
|
55
|
+
readonly ordered: Id[];
|
|
56
|
+
/** Selected and still present, in the order they were picked. */
|
|
57
|
+
readonly picked: Id[];
|
|
58
|
+
/** How many are selected and present. */
|
|
59
|
+
readonly count: number;
|
|
60
|
+
has(id: Id): boolean;
|
|
61
|
+
/** Apply a click with its modifiers (MouseEvent works as is). */
|
|
62
|
+
click(id: Id, mods?: SelectionModifiers): void;
|
|
63
|
+
/** Select only this one (a plain click or a plain arrow move). */
|
|
64
|
+
only(id: Id): void;
|
|
65
|
+
/** Toggle one (Ctrl/⌘ click, or an "x" key). */
|
|
66
|
+
toggle(id: Id): void;
|
|
67
|
+
/** Range from the anchor to `id`; `additive` keeps the rest. */
|
|
68
|
+
extendTo(id: Id, additive?: boolean): void;
|
|
69
|
+
selectAll(): void;
|
|
70
|
+
clear(): void;
|
|
71
|
+
/** Drop ids no longer in `ids()`. Every other method does this first. */
|
|
72
|
+
prune(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Ctrl/⌘ A, Esc (while something is selected) and Shift+arrows (with
|
|
75
|
+
* `next`). Returns true and prevents the default when it handled the key.
|
|
76
|
+
*/
|
|
77
|
+
handleKeydown(e: KeyboardEvent | SelectionKeyEvent, opts?: ClickSelectionKeyOptions<Id>): boolean;
|
|
78
|
+
}
|
|
79
|
+
export declare function createClickSelection<Id = string>(options: ClickSelectionOptions<Id>): ClickSelection<Id>;
|