@heyramzi/ui 2.11.1 → 2.11.2
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/agents/helpers.js +38 -38
- package/dist/agents/page-state.js +49 -50
- package/dist/agents/vendor/color.js +146 -127
- package/dist/agents/vendor/draw.js +617 -508
- package/dist/agents/vendor/engine.js +698 -599
- package/dist/agents/vendor/mesh.js +653 -683
- package/dist/agents/vendor/plastic.js +3913 -3112
- package/dist/agents/vendor/presets.js +163 -164
- package/dist/agents/vendor/shapes.js +21 -20
- package/dist/agents/vendor/ticker.js +41 -42
- package/dist/agents/vendor/types.js +1 -0
- package/dist/agents/vendor/wear.js +1421 -1448
- package/dist/ai/confirmation/confirmation-context.svelte.js +6 -6
- package/dist/ai/confirmation/index.js +3 -1
- package/dist/ai/index.js +33 -0
- package/dist/ai/prompt-input/index.js +3 -1
- package/dist/ai/reasoning/index.js +3 -1
- package/dist/ai/reasoning/reasoning-context.svelte.js +6 -6
- package/dist/ai/suggestion/index.js +3 -1
- package/dist/ai/typing-dots/index.js +3 -1
- package/dist/animations/text-swap.js +24 -24
- package/dist/brand/index.js +54 -48
- package/dist/canvas/card/resize.js +5 -3
- package/dist/canvas/edges/edge-path.js +255 -242
- package/dist/canvas/editor/context-menu.js +39 -36
- package/dist/canvas/editor/dispatch.svelte.js +154 -151
- package/dist/canvas/editor/editor-context.js +7 -2
- package/dist/canvas/editor/editor-mode.js +1 -0
- package/dist/canvas/editor/frame.js +73 -76
- package/dist/canvas/editor/hand-context.js +5 -2
- package/dist/canvas/editor/history-core.js +163 -191
- package/dist/canvas/editor/keymap.js +95 -169
- package/dist/canvas/editor/layout-actions.js +143 -144
- package/dist/canvas/editor/lock.js +52 -51
- package/dist/canvas/editor/rebase.js +217 -240
- package/dist/canvas/editor/style.js +132 -137
- package/dist/canvas/editor/text-field-context.js +7 -2
- package/dist/canvas/editor/z-order.js +65 -70
- package/dist/canvas/headless/alignment.js +22 -16
- package/dist/canvas/headless/break-apart-find.js +286 -279
- package/dist/canvas/headless/break-apart-pieces.js +62 -58
- package/dist/canvas/headless/break-apart-plan.js +299 -325
- package/dist/canvas/headless/break-apart-style.js +75 -70
- package/dist/canvas/headless/break-apart.js +188 -181
- package/dist/canvas/headless/canvas-cleanup.js +78 -58
- package/dist/canvas/headless/edge-anchors.js +115 -116
- package/dist/canvas/headless/edge-memo.js +24 -22
- package/dist/canvas/headless/edge-serialization.js +17 -27
- package/dist/canvas/headless/figure-geometry.js +232 -264
- package/dist/canvas/headless/fit-viewport.js +84 -67
- package/dist/canvas/headless/grid.js +88 -86
- package/dist/canvas/headless/icon-node.js +44 -36
- package/dist/canvas/headless/index.js +1 -0
- package/dist/canvas/headless/linkify.js +17 -19
- package/dist/canvas/headless/pen-marks.js +101 -95
- package/dist/canvas/headless/sketch.js +231 -209
- package/dist/canvas/headless/snap-guides.js +99 -83
- package/dist/canvas/headless/svg-figure.js +133 -117
- package/dist/canvas/headless/table-layout.js +146 -107
- package/dist/canvas/index.js +2 -0
- package/dist/canvas/lod/lod-mode.svelte.js +20 -18
- package/dist/canvas/lod/render-all.svelte.js +36 -39
- package/dist/canvas/ops/arrange.js +78 -71
- package/dist/canvas/ops/ops.js +260 -255
- package/dist/canvas/pen/colors.js +37 -35
- package/dist/canvas/pen/eraser.js +28 -27
- package/dist/canvas/pen/geometry.js +120 -130
- package/dist/canvas/pen/index.js +1 -0
- package/dist/canvas/pen/ops.js +11 -11
- package/dist/canvas/pen/pen-session.js +150 -159
- package/dist/canvas/pen/plugin.js +107 -104
- package/dist/canvas/pen/shapes.js +73 -68
- package/dist/canvas/pen/stroke-path.js +32 -30
- package/dist/canvas/pen/types.js +21 -21
- package/dist/canvas/pen/ui.js +10 -8
- package/dist/canvas/plugin.js +46 -46
- package/dist/canvas/screen/glyphs.js +12 -11
- package/dist/canvas/screen/labels.js +22 -22
- package/dist/canvas/screen/parts.js +98 -111
- package/dist/canvas/screen/plugin.js +47 -56
- package/dist/canvas/screen/types.js +30 -35
- package/dist/canvas/screen/ui.js +7 -7
- package/dist/canvas/test-utils.js +14 -14
- package/dist/canvas/types.js +1 -0
- package/dist/canvas/ui/forward-wheel.js +24 -25
- package/dist/canvas/ui/icons.js +64 -131
- package/dist/canvas/ui/place-panel.js +58 -49
- package/dist/canvas/ui/shortcut-tip.js +14 -12
- package/dist/canvas/ui/tools.js +300 -385
- package/dist/charts/dashboard/chart.js +4 -0
- package/dist/charts/dashboard/data-table.js +11 -0
- package/dist/charts/dashboard/windows.js +8 -3
- package/dist/charts/edge-fade.js +10 -5
- package/dist/charts/hover-band.js +16 -11
- package/dist/charts/index.js +0 -1
- package/dist/charts/motion.js +11 -7
- package/dist/charts/primitives/chart-utils.js +12 -12
- package/dist/components/copy/clipboard.js +32 -32
- package/dist/components/copy/copied.svelte.js +31 -23
- package/dist/components/data-table/data-table.js +39 -39
- package/dist/components/docs/index.js +4 -0
- package/dist/components/docs/scroll-spy.svelte.js +73 -68
- package/dist/components/list-page/filter-section.js +4 -1
- package/dist/components/list-page/index.js +2 -0
- package/dist/components/list-page/list-table.js +7 -2
- package/dist/components/list-page/queue-rail.js +14 -0
- package/dist/components/list-page/section-host.js +4 -2
- package/dist/components/list-page/step.js +4 -3
- package/dist/components/list-page/types.js +1 -0
- package/dist/components/picker/trigger.js +48 -30
- package/dist/components/picker/types.js +1 -0
- package/dist/components/segmented/collapse.js +38 -27
- package/dist/components/segmented/index.js +8 -0
- package/dist/components/segmented/segmented.js +67 -42
- package/dist/components/star-rating-input/index.js +3 -1
- package/dist/components/table-view/types.js +1 -0
- package/dist/components/tags-input/tags.js +81 -69
- package/dist/components/theme/index.js +4 -0
- package/dist/components/toolbar/index.js +4 -0
- package/dist/components/toolbar/toolbar-trigger.js +14 -1
- package/dist/editor/anchors.js +17 -13
- package/dist/editor/block-id.js +52 -51
- package/dist/editor/block-ops.js +108 -80
- package/dist/editor/doc-blocks.js +155 -166
- package/dist/editor/extensions.js +139 -180
- package/dist/editor/index.js +4 -0
- package/dist/editor/markdown-test-helpers.js +4 -2
- package/dist/editor/markdown.js +420 -408
- package/dist/editor/serializer.js +204 -200
- package/dist/editor/slash-items.js +89 -101
- package/dist/editor/youtube-url.js +25 -20
- package/dist/effects/animated-beam/path-calculator.svelte.js +43 -38
- package/dist/effects/animated-beam/types.js +1 -0
- package/dist/effects/border-beam/css.js +92 -78
- package/dist/effects/border-beam/presets.js +1705 -1720
- package/dist/effects/border-beam/register-properties.js +43 -33
- package/dist/effects/border-beam/types.js +1 -0
- package/dist/effects/image-comparison/compare.js +3 -2
- package/dist/effects/image-reveal/types.js +1 -0
- package/dist/effects/magnetic/magnetic.js +58 -52
- package/dist/effects/reveal/observe.js +50 -40
- package/dist/effects/reveal/types.js +1 -0
- package/dist/effects/shader-gradient/color.js +36 -29
- package/dist/effects/shader-gradient/renderer.js +384 -295
- package/dist/effects/shader-gradient/shaders.js +31 -0
- package/dist/effects/shader-gradient/types.js +1 -0
- package/dist/icons/apple-symbol-exports.js +99 -124
- package/dist/icons/sf-symbols.js +357 -461
- package/dist/icons/trades.js +43 -28
- package/dist/inbox/index.js +1 -0
- package/dist/inbox/logic.js +126 -122
- package/dist/inbox/types.js +43 -43
- package/dist/lib/arrive.js +19 -12
- package/dist/lib/icon-types.js +1 -0
- package/dist/lib/initials.js +16 -10
- package/dist/lib/layout.js +5 -0
- package/dist/lib/panel.js +13 -0
- package/dist/lib/result.js +22 -22
- package/dist/lib/sliding-pill.js +59 -44
- package/dist/lib/theme-transition.js +43 -43
- package/dist/lib/tone.js +31 -24
- package/dist/lib/utils.js +3 -1
- package/dist/onboarding/define-tours.js +24 -23
- package/dist/onboarding/onboarding.svelte.js +16 -12
- package/dist/onboarding/pick.js +10 -6
- package/dist/onboarding/stores.js +40 -41
- package/dist/onboarding/wait.js +24 -23
- package/dist/panel/side-panel-types.js +1 -0
- package/dist/record/demo-data.js +459 -486
- package/dist/record/fields.js +129 -131
- package/dist/record/format.js +70 -82
- package/dist/record/gutter.js +1 -0
- package/dist/record/kinds.js +95 -105
- package/dist/record/logic.js +238 -219
- package/dist/record/types.js +49 -48
- package/dist/shadcn/accordion/index.js +3 -1
- package/dist/shadcn/alert/index.js +3 -1
- package/dist/shadcn/alert-dialog/index.js +3 -1
- package/dist/shadcn/attachment/index.js +3 -1
- package/dist/shadcn/avatar/index.js +3 -1
- package/dist/shadcn/breadcrumb/index.js +3 -1
- package/dist/shadcn/bubble/index.js +3 -1
- package/dist/shadcn/button/index.js +3 -1
- package/dist/shadcn/calendar/index.js +3 -1
- package/dist/shadcn/card/index.js +3 -1
- package/dist/shadcn/checkbox/index.js +3 -1
- package/dist/shadcn/code-block/index.js +3 -1
- package/dist/shadcn/collapsible/index.js +3 -1
- package/dist/shadcn/command/index.js +4 -1
- package/dist/shadcn/context-menu/index.js +3 -1
- package/dist/shadcn/dialog/index.js +3 -1
- package/dist/shadcn/dropdown-menu/menu-item.js +40 -20
- package/dist/shadcn/empty/index.js +3 -1
- package/dist/shadcn/field/index.js +3 -1
- package/dist/shadcn/hooks/highlighter.js +77 -75
- package/dist/shadcn/hooks/is-mobile.svelte.js +3 -3
- package/dist/shadcn/hooks/use-highlighted-code.svelte.js +62 -62
- package/dist/shadcn/input/index.js +3 -1
- package/dist/shadcn/kbd/index.js +3 -1
- package/dist/shadcn/label/index.js +3 -1
- package/dist/shadcn/marker/index.js +3 -1
- package/dist/shadcn/message/index.js +3 -1
- package/dist/shadcn/message-scroller/index.js +3 -1
- package/dist/shadcn/popover/index.js +3 -1
- package/dist/shadcn/progress/index.js +3 -1
- package/dist/shadcn/progress/tone.js +8 -7
- package/dist/shadcn/radio-group/index.js +3 -1
- package/dist/shadcn/scroll-area/index.js +3 -1
- package/dist/shadcn/select/index.js +3 -1
- package/dist/shadcn/separator/index.js +3 -1
- package/dist/shadcn/sheet/index.js +3 -1
- package/dist/shadcn/sidebar/context.svelte.js +42 -28
- package/dist/shadcn/sidebar/index.js +3 -1
- package/dist/shadcn/skeleton/index.js +3 -1
- package/dist/shadcn/slider/index.js +3 -1
- package/dist/shadcn/switch/index.js +3 -1
- package/dist/shadcn/table/index.js +3 -1
- package/dist/shadcn/tabs/index.js +3 -1
- package/dist/shadcn/textarea/index.js +3 -1
- package/dist/shadcn/toggle/index.js +3 -1
- package/dist/shadcn/toggle-group/index.js +3 -1
- package/dist/shadcn/tooltip/context.js +7 -2
- package/dist/shadcn/tooltip/index.js +3 -1
- package/dist/shadcn/trace/index.js +3 -1
- package/dist/slides/bridges.js +65 -59
- package/dist/slides/deck-controller.svelte.js +210 -207
- package/dist/slides/export/capture.js +66 -71
- package/dist/slides/export/ffmpeg.js +76 -72
- package/dist/slides/export/index.js +3 -0
- package/dist/slides/export/launch.js +9 -6
- package/dist/slides/export/pdf.js +60 -54
- package/dist/slides/export/types.js +1 -0
- package/dist/slides/labels.js +24 -24
- package/dist/slides/layout.js +39 -41
- package/dist/slides/model.js +9 -7
- package/dist/slides/navigation.js +78 -73
- package/dist/slides/types.js +2 -2
- package/dist/status/define-statuses.js +18 -14
- package/dist/status/status-types.js +1 -0
- package/dist/test-setup.js +24 -20
- package/dist/thread/logic.js +73 -77
- package/dist/thread/types.js +21 -21
- package/dist/work/board-order.js +94 -90
- package/dist/work/dates.js +16 -9
- package/dist/work/detail/detail-types.js +18 -18
- package/dist/work/docs/edited.js +32 -29
- package/dist/work/docs/index.js +4 -0
- package/dist/work/docs/labels.js +34 -34
- package/dist/work/docs/menu.js +113 -106
- package/dist/work/docs/outline.js +39 -29
- package/dist/work/gantt/logic.js +211 -235
- package/dist/work/inbox/inbox-logic.js +71 -75
- package/dist/work/inbox/inbox-statuses.js +75 -72
- package/dist/work/inbox/inbox-types.js +1 -0
- package/dist/work/kanban-drag.js +27 -22
- package/dist/work/kanban-types.js +1 -0
- package/dist/work/presets/sales.js +36 -32
- package/dist/work/reorder.js +9 -8
- package/dist/work/task-types.js +1 -0
- package/dist/work/time/duration.js +51 -46
- package/dist/work/time/time-types.js +23 -23
- package/dist/work/use-kanban-dnd.svelte.js +126 -125
- package/package.json +3 -1
- package/src/components/list-page/master-detail.svelte +3 -1
- package/src/components/list-page/rail-row.svelte +4 -4
- package/src/editor/markdown.ts +30 -35
package/dist/icons/trades.js
CHANGED
|
@@ -1,34 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trade icon map. Tabler covers the domain Lucide and Phosphor miss.
|
|
3
|
+
*/
|
|
4
|
+
/*
|
|
5
|
+
* Lucide/Phosphor gap audit (Sept 2026) against the 14 trade pills in the "NOT SURE? TAP WHAT YOU DO" strip. Lucide has `hammer`, `wrench`, `hard-hat`, `bolt`, `brick-wall`,
|
|
6
|
+
*/
|
|
7
|
+
/*
|
|
8
|
+
* `building`, `car`, but no `trowel`, no tiling grid, no `window` cleaning `spray/squeegee`, no `crane`, no dedicated `handyman` toolset. Phosphor has `hammer`, `wrench`, `hard-hat`, `paint-roller`, and also no `trowel`.
|
|
9
|
+
*/
|
|
10
|
+
/*
|
|
11
|
+
* Tabler 3.46 (6184 icons, MIT) has all 14 at a consistent 2px stroke on 24×24: droplet/pipe, bolt, home (roof), tools, key, window,
|
|
12
|
+
*/
|
|
13
|
+
/*
|
|
14
|
+
* car-garage, hammer / wave-saw-tool, helmet, trowel, grid-dots, leaf, car, crane. Svelte 5 port is ` @tabler/icons-svelte-runes` (runes, not the Svelte 4 ` @tabler/icons-svelte`).
|
|
15
|
+
*/
|
|
1
16
|
import { IconBolt, IconCar, IconCarGarage, IconCrane, IconDroplet, IconGridDots, IconHammer, IconHelmet, IconHome, IconKey, IconLeaf, IconTools, IconTrowel, IconWindow } from "@tabler/icons-svelte-runes";
|
|
2
17
|
export const tradeIconMap = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
plumbers: IconDroplet,
|
|
19
|
+
electricians: IconBolt,
|
|
20
|
+
roofers: IconHome,
|
|
21
|
+
handymen: IconTools,
|
|
22
|
+
locksmiths: IconKey,
|
|
23
|
+
"window-cleaners": IconWindow,
|
|
24
|
+
garages: IconCarGarage,
|
|
25
|
+
carpenters: IconHammer,
|
|
26
|
+
builders: IconHelmet,
|
|
27
|
+
plasterers: IconTrowel,
|
|
28
|
+
tilers: IconGridDots,
|
|
29
|
+
gardeners: IconLeaf,
|
|
30
|
+
"mobile-mechanics": IconCar,
|
|
31
|
+
"construction-companies": IconCrane
|
|
17
32
|
};
|
|
18
33
|
export const tradeLabels = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
plumbers: "PLUMBERS",
|
|
35
|
+
electricians: "ELECTRICIANS",
|
|
36
|
+
roofers: "ROOFERS",
|
|
37
|
+
handymen: "HANDYMEN",
|
|
38
|
+
locksmiths: "LOCKSMITHS",
|
|
39
|
+
"window-cleaners": "WINDOW CLEANERS",
|
|
40
|
+
garages: "GARAGES",
|
|
41
|
+
carpenters: "CARPENTERS",
|
|
42
|
+
builders: "BUILDERS",
|
|
43
|
+
plasterers: "PLASTERERS",
|
|
44
|
+
tilers: "TILERS",
|
|
45
|
+
gardeners: "GARDENERS",
|
|
46
|
+
"mobile-mechanics": "MOBILE MECHANICS",
|
|
47
|
+
"construction-companies": "CONSTRUCTION COMPANIES"
|
|
33
48
|
};
|
|
34
49
|
export const tradeSlugs = Object.keys(tradeIconMap);
|
package/dist/inbox/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as ThreadInbox } from "../../src/inbox/thread-inbox.svelte";
|
|
2
2
|
export { NO_FILTER, actorNames, entryLine, filterThreads, inboxKeyOf, isPending, resolveInboxLabels, snoozeChoices, subjectTypes, threadsOf } from "./logic.js";
|
|
3
3
|
export { ALL, DEFAULT_INBOX_LABELS } from "./types.js";
|
|
4
|
+
// The approval card a pinned proposal is drawn with, and the types its `proposal` field carries.
|
|
4
5
|
export { default as InboxCard } from "../../src/work/inbox/inbox-card.svelte";
|
package/dist/inbox/logic.js
CHANGED
|
@@ -1,156 +1,160 @@
|
|
|
1
1
|
import { isOpen, isTyping } from "../work/inbox/inbox-logic.js";
|
|
2
2
|
import { ALL, DEFAULT_INBOX_LABELS } from "./types.js";
|
|
3
|
+
/** A timestamp as a number, with NaN for one that doesn't parse. */
|
|
3
4
|
function ms(at) {
|
|
4
|
-
|
|
5
|
+
return Date.parse(at);
|
|
5
6
|
}
|
|
6
7
|
function latestOf(thread) {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const time = ms(thread.latestAt);
|
|
9
|
+
return Number.isNaN(time) ? Number.NEGATIVE_INFINITY : time;
|
|
9
10
|
}
|
|
11
|
+
/** True while a proposal entry still waits for a verdict. */
|
|
10
12
|
export function isPending(entry) {
|
|
11
|
-
|
|
13
|
+
return entry.kind === "proposal" && !!entry.proposal && isOpen(entry.proposal);
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* One thread per subject, newest first. An entry with no subject id is dropped, and a malformed
|
|
17
|
+
* timestamp sorts last.
|
|
18
|
+
*/
|
|
13
19
|
export function threadsOf(entries, options = {}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return bTime - aTime;
|
|
60
|
-
}).map((row)=>row.thread);
|
|
20
|
+
const groups = new Map();
|
|
21
|
+
entries.forEach((entry, index) => {
|
|
22
|
+
const subject = entry.subject;
|
|
23
|
+
if (!subject?.id) return;
|
|
24
|
+
const key = `${subject.type}:${subject.id}`;
|
|
25
|
+
groups.set(key, [...groups.get(key) ?? [], {
|
|
26
|
+
entry,
|
|
27
|
+
subject,
|
|
28
|
+
index,
|
|
29
|
+
time: ms(entry.at)
|
|
30
|
+
}]);
|
|
31
|
+
});
|
|
32
|
+
const threads = [];
|
|
33
|
+
for (const [key, rows] of groups) {
|
|
34
|
+
const ordered = rows.toSorted((a, b) => {
|
|
35
|
+
const aBad = Number.isNaN(a.time);
|
|
36
|
+
const bBad = Number.isNaN(b.time);
|
|
37
|
+
if (aBad !== bBad) return aBad ? 1 : -1;
|
|
38
|
+
return (aBad ? 0 : a.time - b.time) || a.index - b.index;
|
|
39
|
+
});
|
|
40
|
+
const newest = ordered.findLast((row) => !Number.isNaN(row.time)) ?? ordered[0];
|
|
41
|
+
const list = ordered.map((row) => row.entry);
|
|
42
|
+
threads.push({
|
|
43
|
+
key,
|
|
44
|
+
// The newest entry carries the freshest title and status.
|
|
45
|
+
subject: newest.subject,
|
|
46
|
+
entries: list,
|
|
47
|
+
unread: list.filter((entry) => !entry.read).length,
|
|
48
|
+
latestAt: newest.entry.at,
|
|
49
|
+
pending: list.filter(isPending).toReversed()
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return threads.map((thread, index) => ({
|
|
53
|
+
thread,
|
|
54
|
+
index
|
|
55
|
+
})).toSorted((a, b) => {
|
|
56
|
+
if (options.unreadFirst) {
|
|
57
|
+
const unread = Number(b.thread.unread > 0) - Number(a.thread.unread > 0);
|
|
58
|
+
if (unread) return unread;
|
|
59
|
+
}
|
|
60
|
+
const aTime = latestOf(a.thread);
|
|
61
|
+
const bTime = latestOf(b.thread);
|
|
62
|
+
if (aTime === bTime) return a.index - b.index;
|
|
63
|
+
return bTime - aTime;
|
|
64
|
+
}).map((row) => row.thread);
|
|
61
65
|
}
|
|
62
66
|
export const NO_FILTER = {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
type: ALL,
|
|
68
|
+
who: ALL,
|
|
69
|
+
needsYou: false
|
|
66
70
|
};
|
|
71
|
+
/** Keep a thread when it matches the type, holds an entry from the person or agent, and has a pending proposal if asked. */
|
|
67
72
|
export function filterThreads(threads, filter) {
|
|
68
|
-
|
|
73
|
+
return threads.filter((thread) => (filter.type === ALL || thread.subject.type === filter.type) && (filter.who === ALL || thread.entries.some((entry) => entry.actor.name === filter.who)) && (!filter.needsYou || thread.pending.length > 0));
|
|
69
74
|
}
|
|
70
75
|
export function subjectTypes(threads) {
|
|
71
|
-
|
|
72
|
-
...new Set(threads.map((thread)=>thread.subject.type))
|
|
73
|
-
];
|
|
76
|
+
return [...new Set(threads.map((thread) => thread.subject.type))];
|
|
74
77
|
}
|
|
75
78
|
export function actorNames(threads) {
|
|
76
|
-
|
|
77
|
-
...new Set(threads.flatMap((thread)=>thread.entries.map((entry)=>entry.actor.name)))
|
|
78
|
-
].filter(Boolean);
|
|
79
|
+
return [...new Set(threads.flatMap((thread) => thread.entries.map((entry) => entry.actor.name)))].filter(Boolean);
|
|
79
80
|
}
|
|
80
81
|
const KEYS = {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
j: "next",
|
|
83
|
+
k: "previous",
|
|
84
|
+
u: "toggleRead",
|
|
85
|
+
h: "snooze",
|
|
86
|
+
a: "approve",
|
|
87
|
+
d: "decline",
|
|
88
|
+
s: "steer",
|
|
89
|
+
S: "unfollow",
|
|
90
|
+
e: "edit"
|
|
90
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* What a key press asks for, or null when it isn't ours: focus is in a text field, or a modifier
|
|
94
|
+
* other than Linear's Shift+S, Shift+Backspace and Alt+U is held.
|
|
95
|
+
*/
|
|
91
96
|
export function inboxKeyOf(event) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey) return null;
|
|
98
|
+
if (isTyping(event.target)) return null;
|
|
99
|
+
// Option+U on a Mac types a dead key, so the physical key decides.
|
|
100
|
+
if (event.altKey) return event.code === "KeyU" && !event.shiftKey ? "readAll" : null;
|
|
101
|
+
if (event.key === "Backspace") return event.shiftKey ? "clearRead" : "clear";
|
|
102
|
+
return KEYS[event.key] ?? null;
|
|
97
103
|
}
|
|
104
|
+
/** The three times a snooze offers: 3 hours on, tomorrow at 9, next Monday at 9. */
|
|
98
105
|
export function snoozeChoices(now) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
106
|
+
const later = new Date(now.getTime() + 3 * 60 * 60 * 1e3);
|
|
107
|
+
const tomorrow = new Date(now);
|
|
108
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
109
|
+
tomorrow.setHours(9, 0, 0, 0);
|
|
110
|
+
const nextWeek = new Date(now);
|
|
111
|
+
nextWeek.setDate(nextWeek.getDate() + ((8 - nextWeek.getDay()) % 7 || 7));
|
|
112
|
+
nextWeek.setHours(9, 0, 0, 0);
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
id: "later",
|
|
116
|
+
until: later
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: "tomorrow",
|
|
120
|
+
until: tomorrow
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "nextWeek",
|
|
124
|
+
until: nextWeek
|
|
125
|
+
}
|
|
126
|
+
];
|
|
120
127
|
}
|
|
121
128
|
export function resolveInboxLabels(overrides) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
return {
|
|
130
|
+
...DEFAULT_INBOX_LABELS,
|
|
131
|
+
...overrides
|
|
132
|
+
};
|
|
126
133
|
}
|
|
134
|
+
/** One line for an entry, in the host's words. `{from}`, `{to}` and `{assignee}` are filled. */
|
|
127
135
|
export function entryLine(entry, labels) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return labels.responded;
|
|
138
|
-
case "proposal":
|
|
139
|
-
return labels.proposed;
|
|
140
|
-
default:
|
|
141
|
-
return labels.commented;
|
|
142
|
-
}
|
|
136
|
+
const fill = (text) => text.replaceAll("{from}", entry.status?.from ?? "").replaceAll("{to}", entry.status?.to ?? "").replaceAll("{assignee}", entry.assignee ?? "");
|
|
137
|
+
switch (entry.kind) {
|
|
138
|
+
case "status": return fill(entry.status?.from ? labels.statusMoved : labels.statusSet);
|
|
139
|
+
case "assigned": return fill(labels.assigned);
|
|
140
|
+
case "mention": return labels.mentioned;
|
|
141
|
+
case "agent_response": return labels.responded;
|
|
142
|
+
case "proposal": return labels.proposed;
|
|
143
|
+
default: return labels.commented;
|
|
144
|
+
}
|
|
143
145
|
}
|
|
146
|
+
/** An entry as a sentence: who, then what. With no known actor it starts with the verb, capitalised. */
|
|
144
147
|
export function sentenceOf(entry, labels) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
const line = entryLine(entry, labels);
|
|
149
|
+
if (entry.actor.name) return `${entry.actor.name} ${line}`;
|
|
150
|
+
return line.charAt(0).toUpperCase() + line.slice(1);
|
|
148
151
|
}
|
|
152
|
+
/** What happened to the subject, as against something a person wrote. */
|
|
149
153
|
const EVENT_KINDS = new Set([
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
"status",
|
|
155
|
+
"assigned",
|
|
156
|
+
"proposal"
|
|
153
157
|
]);
|
|
154
158
|
export function isEvent(entry) {
|
|
155
|
-
|
|
159
|
+
return EVENT_KINDS.has(entry.kind);
|
|
156
160
|
}
|
package/dist/inbox/types.js
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
export const ALL = "all";
|
|
2
2
|
export const DEFAULT_INBOX_LABELS = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
3
|
+
title: "Inbox",
|
|
4
|
+
all: "All",
|
|
5
|
+
anyone: "Anyone",
|
|
6
|
+
needsYou: "Needs you",
|
|
7
|
+
empty: "Nothing here",
|
|
8
|
+
emptyNeedsYou: "Nothing is waiting on you",
|
|
9
|
+
emptyFiltered: "No thread matches these filters",
|
|
10
|
+
clearFilters: "Clear filters",
|
|
11
|
+
unread: "unread",
|
|
12
|
+
markRead: "Mark read",
|
|
13
|
+
markUnread: "Mark unread",
|
|
14
|
+
snooze: "Snooze",
|
|
15
|
+
snoozeLater: "Later today",
|
|
16
|
+
snoozeTomorrow: "Tomorrow",
|
|
17
|
+
snoozeNextWeek: "Next week",
|
|
18
|
+
snoozed: "Snoozed",
|
|
19
|
+
showSnoozed: "Show snoozed",
|
|
20
|
+
unsnooze: "Unsnooze",
|
|
21
|
+
unfollow: "Unfollow",
|
|
22
|
+
markAllRead: "Mark all read",
|
|
23
|
+
clear: "Clear",
|
|
24
|
+
clearRead: "Clear read",
|
|
25
|
+
selectThread: "Select a thread",
|
|
26
|
+
filterType: "Filter by type",
|
|
27
|
+
filterWho: "Filter by person or agent",
|
|
28
|
+
filter: "Filter",
|
|
29
|
+
kind: "Type",
|
|
30
|
+
from: "From",
|
|
31
|
+
open: "Open",
|
|
32
|
+
agent: "AI",
|
|
33
|
+
statusMoved: "moved this from {from} to {to}",
|
|
34
|
+
statusSet: "set this to {to}",
|
|
35
|
+
assigned: "assigned this to {assignee}",
|
|
36
|
+
mentioned: "mentioned you",
|
|
37
|
+
commented: "commented",
|
|
38
|
+
responded: "replied",
|
|
39
|
+
proposed: "asks for your call",
|
|
40
|
+
hintMove: "move",
|
|
41
|
+
hintRead: "read",
|
|
42
|
+
hintReadAll: "all read",
|
|
43
|
+
hintSnooze: "snooze",
|
|
44
|
+
hintClear: "clear, with shift every read thread",
|
|
45
|
+
hintAct: "approve, decline, steer, edit"
|
|
46
46
|
};
|
package/dist/lib/arrive.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Replays `.t-enter` whenever the value handed in changes, so a panel that swaps
|
|
3
|
+
* its record reads as a new one arriving. `<div use:arrive={selectedId}>`
|
|
4
|
+
*/
|
|
5
|
+
// Prefer it to `{#key}`, which rebuilds the subtree and loses scroll, focus and
|
|
6
|
+
// open disclosures. `use:` works on elements only, so a component still needs `{#key}`.
|
|
7
|
+
export const arrive = (node, value) => {
|
|
8
|
+
let previous = value;
|
|
9
|
+
// The first paint is the mount, which already animates on its own class.
|
|
10
|
+
node.classList.add("t-enter");
|
|
11
|
+
return { update(next) {
|
|
12
|
+
if (next === previous) return;
|
|
13
|
+
previous = next;
|
|
14
|
+
// Remove, force a reflow, re-add. A CSS animation runs once per element,
|
|
15
|
+
// and only a reflow between the two writes makes the browser replay it.
|
|
16
|
+
node.classList.remove("t-enter");
|
|
17
|
+
void node.offsetWidth;
|
|
18
|
+
node.classList.add("t-enter");
|
|
19
|
+
} };
|
|
13
20
|
};
|
package/dist/lib/icon-types.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/lib/initials.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
const segmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter() : null;
|
|
2
|
+
/** The first whole character of a string: an emoji or an accented letter stays in one piece. */
|
|
2
3
|
function firstGrapheme(text) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
if (!text) return "";
|
|
5
|
+
if (segmenter) return segmenter.segment(text)[Symbol.iterator]().next().value?.segment ?? "";
|
|
6
|
+
return Array.from(text)[0] ?? "";
|
|
6
7
|
}
|
|
8
|
+
/** One letter for an avatar fallback. Falls back to `fallback` for an empty or missing name. */
|
|
7
9
|
export function initial(name, fallback = "?") {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
const first = firstGrapheme((name ?? "").trim());
|
|
11
|
+
return first ? first.toLocaleUpperCase() : fallback;
|
|
10
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Two letters for an avatar fallback, first and last word, skipping suffixes: "Christopher
|
|
15
|
+
* Montgomery III" gives "CM". A one-word name gives one letter.
|
|
16
|
+
*/
|
|
11
17
|
export function initials(name, fallback = "?") {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
const words = (name ?? "").trim().split(/\s+/).filter((word) => word && !/^(?:[IVX]{1,4}|jr\.?|sr\.?)$/i.test(word));
|
|
19
|
+
if (words.length === 0) return initial(name, fallback);
|
|
20
|
+
const head = firstGrapheme(words[0]);
|
|
21
|
+
const tail = words.length > 1 ? firstGrapheme(words[words.length - 1]) : "";
|
|
22
|
+
return (head + tail).toLocaleUpperCase();
|
|
17
23
|
}
|
package/dist/lib/layout.js
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
+
/** The app's one horizontal gutter. */
|
|
2
|
+
// WHY a token: the tab strip said `px-4 md:px-6` and page bodies `p-4`, so from
|
|
3
|
+
// `md` up the nav sat 8px right of the content. Same fact typed twice.
|
|
4
|
+
// The content edge won: sixty page bodies against one strip. Anything lining up
|
|
5
|
+
// with the page body names this instead of retyping a padding.
|
|
1
6
|
export const pageGutterClass = "px-4";
|
package/dist/lib/panel.js
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/** The one panel shell: a titled box holding a table, a chart or a stack. */
|
|
2
|
+
// WHY tokens, not markup: `DetailPanel` and `DataPanel` draw the same box with
|
|
3
|
+
// different props, and their edges drifted because the classes were typed twice.
|
|
4
|
+
// Dashboards used `Card`, so /ads had a ring and a shadow where /social had a
|
|
5
|
+
// hairline. Flat, one hairline, `rounded-xl`.
|
|
1
6
|
export const panelShellClass = "overflow-hidden rounded-xl border border-border bg-card";
|
|
7
|
+
/**
|
|
8
|
+
* The panel's header bar: label left, count or controls right. `min-h-11` so a
|
|
9
|
+
* label-only header matches one with a button.
|
|
10
|
+
*/
|
|
2
11
|
export const panelHeaderClass = "flex min-h-11 flex-wrap items-center justify-between gap-x-3 gap-y-1 border-b border-border px-4 py-2";
|
|
12
|
+
/**
|
|
13
|
+
* The inset well for a control block inside a panel. A visible step at the
|
|
14
|
+
* same depth, for filter rows and grouped controls.
|
|
15
|
+
*/
|
|
3
16
|
export const insetWellClass = "rounded-lg border border-border bg-muted/40 p-2";
|
package/dist/lib/result.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
export function tryCatch(fn) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
try {
|
|
3
|
+
return {
|
|
4
|
+
ok: true,
|
|
5
|
+
value: fn()
|
|
6
|
+
};
|
|
7
|
+
} catch (e) {
|
|
8
|
+
return {
|
|
9
|
+
ok: false,
|
|
10
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
13
|
}
|
|
14
14
|
export async function tryCatchAsync(fn) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
try {
|
|
16
|
+
return {
|
|
17
|
+
ok: true,
|
|
18
|
+
value: await fn()
|
|
19
|
+
};
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
26
|
}
|