@agent-native/core-corpus 0.209.1-nightly-20261015153252 → 0.209.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +2 -0
- package/corpus/templates/analytics/CHANGELOG.md +25 -0
- package/corpus/templates/analytics/actions/update-dashboard.ts +22 -2
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/i18n/en-US.ts +1 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +52 -7
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +79 -10
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +7 -0
- package/corpus/templates/analytics/changelog/2026-10-14-multi-select-dashboard-filters-can-offer-presets-named-group.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-collapsed-navigation-has-clearer-spacing-between-its-icons.md +6 -0
- package/corpus/templates/clips/CHANGELOG.md +29 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +16 -0
- package/corpus/templates/clips/changelog/2026-10-15-clips-stays-labeled-in-the-menu-bar-while-idle.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-15-earlier-screen-time-stays-available-across-capture-mode-chan.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-15-rewind-can-be-enabled-from-include-last-without-leaving-the-recorder.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-15-screen-memory-checks-privacy-exclusions-during-capture-recov.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-15-screen-memory-retries-capture-promptly-after-audio-mode-chan.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-15-screen-memory-retries-recovery-after-suspension-and-audio-mode-change.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-15-the-recording-countdown-starts-when-it-appears-and-plays-smo.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +144 -54
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +166 -33
- package/corpus/templates/clips/desktop/src/lib/rewind-start-error.ts +48 -0
- package/corpus/templates/clips/desktop/src/lookback/LookbackEditDialog.tsx +78 -25
- package/corpus/templates/clips/desktop/src/lookback/worker.ts +5 -0
- package/corpus/templates/clips/desktop/src/main.tsx +1 -1
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +127 -7
- package/corpus/templates/clips/desktop/src/overlays/record-pill.tsx +16 -3
- package/corpus/templates/clips/desktop/src/styles.css +88 -4
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +157 -43
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +35 -1
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_capture_suspension.rs +3 -2
- package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +1074 -85
- package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +181 -47
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +2 -1
- package/corpus/templates/content/CHANGELOG.md +1 -0
- package/corpus/templates/design/CHANGELOG.md +18 -0
- package/corpus/templates/dispatch/CHANGELOG.md +6 -0
- package/corpus/templates/mail/CHANGELOG.md +7 -0
- package/corpus/templates/slides/AGENTS.md +1 -0
- package/corpus/templates/slides/CHANGELOG.md +27 -0
- package/corpus/templates/slides/actions/add-slide.ts +2 -0
- package/corpus/templates/slides/app/context/DeckContext.tsx +7 -1
- package/corpus/templates/slides/app/global.css +1 -3
- package/corpus/templates/slides/app/hooks/use-agent-generating.ts +1 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +5 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +186 -35
- package/corpus/templates/slides/app/pages/Index.tsx +75 -68
- package/corpus/templates/slides/changelog/2026-10-14-fixed-retries-so-the-original-deck-request-stays-available-i.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-14-slides-keeps-prompt-available-if-a-retry-never-starts.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-15-blank-decks-and-simple-follow-ups-like-adding-one-empty-slid.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-15-selected-slide-elements-now-show-a-single-blue-outline.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-15-slides-shows-fallback-prompts-immediately-and-persists-gener.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-15-suggestion-chips-have-more-breathing-room-above-the-chat-fie.md +6 -0
- package/package.json +1 -1
- package/corpus/templates/analytics/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/assets/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/brain/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/calendar/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/chat/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/clips/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/content/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/crm/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/design/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/dispatch/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/forms/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/mail/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/plan/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
- package/corpus/templates/slides/.deploy-tmp/agent-native-provided-plugins.mjs +0 -10
package/corpus/README.md
CHANGED
|
@@ -382,6 +382,8 @@ Use conditional blocks for optional filters:
|
|
|
382
382
|
|
|
383
383
|
An unwrapped `{{<id>:list}}` with no selection fails the query on purpose. Option values must not contain commas.
|
|
384
384
|
|
|
385
|
+
To offer a named group of values, give the multi-select `presets`, such as `"presets": [{ "label": "Launch apps", "values": ["clips", "slides", "design"] }]`. Each preset shows as a button beside Select all in the dropdown, and picking it replaces only that filter's selection, so the other filters keep their values. Every value must be one of the filter's options. Use a preset rather than a saved view when the user wants a shortcut for one filter: a saved view replaces every filter.
|
|
386
|
+
|
|
385
387
|
Filters auto-apply on change — there is no Apply button. Each filter change writes to the URL and re-runs the affected panels. Other filters are preserved (the URL update is functional, not destructive). If you see a filter "reset" itself when another filter changes, look for a duplicate `id` first.
|
|
386
388
|
|
|
387
389
|
**Reset filters** in the filter bar returns the dashboard to what a fresh load shows: the default saved view when the dashboard has one, otherwise each filter's own `default`. It shows only while a filter's resolved value differs from that, so `f_timeRange=30d` on a 30d-default filter counts as reset. To reset for the user with `set-search-params`, remove every `f_` param and `view`, or set the default view's params and `view=<id>`.
|
|
@@ -3,6 +3,31 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Analytics are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
5
5
|
|
|
6
|
+
## 2026-10-15
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Collapsed navigation has clearer spacing between its icons
|
|
11
|
+
|
|
12
|
+
## 2026-10-14
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Multi-select dashboard filters can offer presets, named groups of values you pick in one click without changing the other filters.
|
|
17
|
+
- Preview Sigma workbooks in Analytics; SQL import stays blocked until the BigQuery project mapping can be verified.
|
|
18
|
+
|
|
19
|
+
### Improved
|
|
20
|
+
|
|
21
|
+
- Dashboards show a Reset filters button whenever a filter differs from the defaults, and it returns every filter to them in one click.
|
|
22
|
+
- Gong questions use Gong's own search and answer in seconds instead of paging through every call.
|
|
23
|
+
- Start Sigma workbook imports from the dashboard menu, use an Analytics-only connection with direct credentials, or choose a Sigma connection already granted through Dispatch.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Chats no longer stop with "result is unknown" after a rejected edit; the agent corrects the call and continues.
|
|
28
|
+
- Fixed the chat repeating a finished reply several times and then replacing it with "I couldn't complete that lookup" after a correct dashboard edit.
|
|
29
|
+
- Membership searches no longer treat monthly counts as user rosters
|
|
30
|
+
|
|
6
31
|
## 2026-10-13
|
|
7
32
|
|
|
8
33
|
### Added
|
|
@@ -11,6 +11,8 @@ import { z } from "zod";
|
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
FILTER_TYPES,
|
|
14
|
+
filterPresetIssues,
|
|
15
|
+
MAX_FILTER_OPTIONS,
|
|
14
16
|
MULTI_SELECT_EMPTY,
|
|
15
17
|
normalizeMultiSelectValue,
|
|
16
18
|
} from "../app/pages/adhoc/sql-dashboard/filter-vars";
|
|
@@ -307,9 +309,9 @@ function collectDashboardConfigIssues(
|
|
|
307
309
|
);
|
|
308
310
|
}
|
|
309
311
|
// The read path (isDashboardFilter) rejects more than 100 options, so a save that passes here would make the dashboard unreadable.
|
|
310
|
-
if (Array.isArray(f.options) && f.options.length >
|
|
312
|
+
if (Array.isArray(f.options) && f.options.length > MAX_FILTER_OPTIONS) {
|
|
311
313
|
return dashboardIssue(
|
|
312
|
-
`config.filters[${i}].options can have at most
|
|
314
|
+
`config.filters[${i}].options can have at most ${MAX_FILTER_OPTIONS} entries`,
|
|
313
315
|
);
|
|
314
316
|
}
|
|
315
317
|
// isDashboardFilter rejects these shapes for every filter type, so a save that passes here would drop the whole filter set on read.
|
|
@@ -368,6 +370,24 @@ function collectDashboardConfigIssues(
|
|
|
368
370
|
}
|
|
369
371
|
}
|
|
370
372
|
}
|
|
373
|
+
if (f.presets !== undefined) {
|
|
374
|
+
// isDashboardFilter rejects a malformed preset, so a save that passes here would drop the whole filter set on read.
|
|
375
|
+
if (f.type !== "multi-select" || !Array.isArray(f.presets)) {
|
|
376
|
+
return dashboardIssue(
|
|
377
|
+
`config.filters[${i}].presets must be an array, and only multi-select filters take presets`,
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
const optionValues = new Set(
|
|
381
|
+
options.map((option) => (option as { value: string }).value),
|
|
382
|
+
);
|
|
383
|
+
const issues = filterPresetIssues(f.presets, optionValues);
|
|
384
|
+
const j = issues.findIndex((issue) => issue !== null);
|
|
385
|
+
if (j !== -1) {
|
|
386
|
+
return dashboardIssue(
|
|
387
|
+
`config.filters[${i}].presets[${j}] ${issues[j]}`,
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
371
391
|
if (seen.has(id)) continue;
|
|
372
392
|
seen.add(id);
|
|
373
393
|
deduped.push(f);
|
|
@@ -2472,7 +2472,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2472
2472
|
brandHref="/home"
|
|
2473
2473
|
collapsed={true}
|
|
2474
2474
|
/>
|
|
2475
|
-
<nav className="flex min-h-0 flex-1 flex-col items-center gap-
|
|
2475
|
+
<nav className="flex min-h-0 flex-1 flex-col items-center gap-2 overflow-y-auto px-2 py-3">
|
|
2476
2476
|
{collapsedNavItems.map((item) => {
|
|
2477
2477
|
const Icon = item.icon;
|
|
2478
2478
|
return (
|
|
@@ -864,6 +864,7 @@ export default {
|
|
|
864
864
|
selectAll: "Select all",
|
|
865
865
|
selectOnly: "Only",
|
|
866
866
|
selectOnlyValue: "Only {{value}}",
|
|
867
|
+
presets: "Presets",
|
|
867
868
|
untitledDashboard: "Untitled Dashboard",
|
|
868
869
|
dashboardFallback: "dashboard",
|
|
869
870
|
viewOnly: "You have view-only access to this dashboard.",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
2
|
import {
|
|
3
|
+
IconCheck,
|
|
3
4
|
IconChevronDown,
|
|
4
5
|
IconDeviceFloppy,
|
|
5
6
|
IconFilterOff,
|
|
@@ -46,8 +47,9 @@ import {
|
|
|
46
47
|
MULTI_SELECT_EMPTY,
|
|
47
48
|
resolveDefault,
|
|
48
49
|
resolveFilterVars,
|
|
50
|
+
usableFilterPresets,
|
|
49
51
|
} from "./filter-vars";
|
|
50
|
-
import type { DashboardFilter } from "./types";
|
|
52
|
+
import type { DashboardFilter, FilterPreset } from "./types";
|
|
51
53
|
|
|
52
54
|
export { FILTER_PARAM_PREFIX, resolveFilterVars } from "./filter-vars";
|
|
53
55
|
|
|
@@ -445,6 +447,11 @@ function MultiSelectFilter({
|
|
|
445
447
|
options.find((option) => option.value === selectedValue)?.label ??
|
|
446
448
|
selectedValue,
|
|
447
449
|
);
|
|
450
|
+
const presets = usableFilterPresets(filter.presets, seenOptionValues);
|
|
451
|
+
const presetSelected = (preset: FilterPreset) =>
|
|
452
|
+
new Set(preset.values).size === selectedSet.size &&
|
|
453
|
+
preset.values.every((presetValue) => selectedSet.has(presetValue));
|
|
454
|
+
const selectedPreset = presets.find(presetSelected);
|
|
448
455
|
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
449
456
|
const filteredOptions = normalizedQuery
|
|
450
457
|
? options.filter(
|
|
@@ -471,6 +478,11 @@ function MultiSelectFilter({
|
|
|
471
478
|
]),
|
|
472
479
|
),
|
|
473
480
|
);
|
|
481
|
+
const applyPreset = (preset: FilterPreset) => {
|
|
482
|
+
setSelected(Array.from(new Set(preset.values)));
|
|
483
|
+
setOpen(false);
|
|
484
|
+
setQuery("");
|
|
485
|
+
};
|
|
474
486
|
const toggle = (optionValue: string, checked: boolean) =>
|
|
475
487
|
setSelected(
|
|
476
488
|
checked
|
|
@@ -501,13 +513,19 @@ function MultiSelectFilter({
|
|
|
501
513
|
<span className="min-w-0 truncate">
|
|
502
514
|
{allOptionsSelected
|
|
503
515
|
? t("sqlDashboard.allValues")
|
|
504
|
-
:
|
|
505
|
-
?
|
|
506
|
-
:
|
|
516
|
+
: selectedPreset
|
|
517
|
+
? selectedPreset.label
|
|
518
|
+
: selectedLabels.length > 0
|
|
519
|
+
? selectedLabels.join(", ")
|
|
520
|
+
: t("sqlDashboard.allValues")}
|
|
507
521
|
</span>
|
|
508
522
|
</Button>
|
|
509
523
|
</PopoverTrigger>
|
|
510
|
-
<PopoverContent
|
|
524
|
+
<PopoverContent
|
|
525
|
+
align="start"
|
|
526
|
+
// Room for Select all, a preset and Clear all on one row.
|
|
527
|
+
className={presets.length > 0 ? "w-72" : "w-64"}
|
|
528
|
+
>
|
|
511
529
|
<MenuSearchInput
|
|
512
530
|
aria-controls={optionsListId}
|
|
513
531
|
aria-label={t("sqlDashboard.searchValues")}
|
|
@@ -515,7 +533,7 @@ function MultiSelectFilter({
|
|
|
515
533
|
value={query}
|
|
516
534
|
onChange={(event) => setQuery(event.currentTarget.value)}
|
|
517
535
|
/>
|
|
518
|
-
<div className="flex items-center
|
|
536
|
+
<div className="flex flex-wrap items-center gap-x-1 gap-y-1 border-b px-2 py-1.5">
|
|
519
537
|
<Button
|
|
520
538
|
type="button"
|
|
521
539
|
variant="ghost"
|
|
@@ -525,12 +543,39 @@ function MultiSelectFilter({
|
|
|
525
543
|
>
|
|
526
544
|
{t("sqlDashboard.selectAll")}
|
|
527
545
|
</Button>
|
|
546
|
+
{presets.length > 0 && !normalizedQuery && (
|
|
547
|
+
<div
|
|
548
|
+
role="group"
|
|
549
|
+
aria-label={t("sqlDashboard.presets")}
|
|
550
|
+
className="flex min-w-0 flex-wrap items-center gap-x-1 gap-y-1"
|
|
551
|
+
>
|
|
552
|
+
{presets.map((preset) => {
|
|
553
|
+
const active = presetSelected(preset);
|
|
554
|
+
return (
|
|
555
|
+
<Button
|
|
556
|
+
key={preset.label}
|
|
557
|
+
type="button"
|
|
558
|
+
variant="ghost"
|
|
559
|
+
size="xs"
|
|
560
|
+
aria-pressed={active}
|
|
561
|
+
className={cn(
|
|
562
|
+
"min-w-0 max-w-full",
|
|
563
|
+
active && "bg-accent",
|
|
564
|
+
)}
|
|
565
|
+
onClick={() => applyPreset(preset)}
|
|
566
|
+
>
|
|
567
|
+
{active && <IconCheck />}
|
|
568
|
+
<span className="min-w-0 truncate">{preset.label}</span>
|
|
569
|
+
</Button>
|
|
570
|
+
);
|
|
571
|
+
})}
|
|
572
|
+
</div>
|
|
573
|
+
)}
|
|
528
574
|
{selected.length > 0 && (
|
|
529
575
|
<Button
|
|
530
576
|
type="button"
|
|
531
577
|
variant="ghost"
|
|
532
578
|
size="xs"
|
|
533
|
-
className="w-full justify-start"
|
|
534
579
|
onClick={() => setSelected([])}
|
|
535
580
|
>
|
|
536
581
|
{t("sqlDashboard.clearAll")}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DashboardFilter, FilterType } from "./types";
|
|
1
|
+
import type { DashboardFilter, FilterPreset, FilterType } from "./types";
|
|
2
2
|
|
|
3
3
|
export const FILTER_PARAM_PREFIX = "f_";
|
|
4
4
|
|
|
@@ -40,6 +40,10 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
40
40
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export const MAX_FILTER_OPTIONS = 100;
|
|
44
|
+
// Each preset is a button in the dropdown, so a filter offers a short list.
|
|
45
|
+
export const MAX_FILTER_PRESETS = 20;
|
|
46
|
+
|
|
43
47
|
export function isDashboardFilter(value: unknown): value is DashboardFilter {
|
|
44
48
|
if (
|
|
45
49
|
!isRecord(value) ||
|
|
@@ -52,15 +56,80 @@ export function isDashboardFilter(value: unknown): value is DashboardFilter {
|
|
|
52
56
|
return false;
|
|
53
57
|
}
|
|
54
58
|
return (
|
|
55
|
-
value.options === undefined ||
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
(value.options === undefined ||
|
|
60
|
+
(Array.isArray(value.options) &&
|
|
61
|
+
value.options.length <= MAX_FILTER_OPTIONS &&
|
|
62
|
+
value.options.every(
|
|
63
|
+
(option) =>
|
|
64
|
+
isRecord(option) &&
|
|
65
|
+
typeof option.value === "string" &&
|
|
66
|
+
typeof option.label === "string",
|
|
67
|
+
))) &&
|
|
68
|
+
(value.presets === undefined ||
|
|
69
|
+
(Array.isArray(value.presets) && value.presets.every(isFilterPreset)))
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function isFilterPreset(value: unknown): value is FilterPreset {
|
|
74
|
+
return (
|
|
75
|
+
isRecord(value) &&
|
|
76
|
+
typeof value.label === "string" &&
|
|
77
|
+
Array.isArray(value.values) &&
|
|
78
|
+
value.values.every((v) => typeof v === "string")
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// The one usability check for presets, aligned with the input: null for a
|
|
83
|
+
// usable preset, else why not. Saves reject the first issue. The filter bar
|
|
84
|
+
// skips each unusable preset, because a config edited through the shared
|
|
85
|
+
// document reaches it unchecked and isDashboardFilter stays structural so it
|
|
86
|
+
// never drops the whole filter set.
|
|
87
|
+
export function filterPresetIssues(
|
|
88
|
+
presets: readonly unknown[],
|
|
89
|
+
optionValues: ReadonlySet<string>,
|
|
90
|
+
): (string | null)[] {
|
|
91
|
+
const labels = new Set<string>();
|
|
92
|
+
return presets.map((preset, index) => {
|
|
93
|
+
if (index >= MAX_FILTER_PRESETS) {
|
|
94
|
+
return `is past the limit of ${MAX_FILTER_PRESETS} presets`;
|
|
95
|
+
}
|
|
96
|
+
// Before isFilterPreset walks the values, so an oversized list costs nothing.
|
|
97
|
+
if (
|
|
98
|
+
isRecord(preset) &&
|
|
99
|
+
Array.isArray(preset.values) &&
|
|
100
|
+
preset.values.length > MAX_FILTER_OPTIONS
|
|
101
|
+
) {
|
|
102
|
+
return `has more than ${MAX_FILTER_OPTIONS} values`;
|
|
103
|
+
}
|
|
104
|
+
if (
|
|
105
|
+
!isFilterPreset(preset) ||
|
|
106
|
+
preset.label.trim() === "" ||
|
|
107
|
+
preset.values.length === 0
|
|
108
|
+
) {
|
|
109
|
+
return "must be an object with a non-empty label and a non-empty values array";
|
|
110
|
+
}
|
|
111
|
+
if (labels.has(preset.label)) {
|
|
112
|
+
return `label "${preset.label}" repeats an earlier preset`;
|
|
113
|
+
}
|
|
114
|
+
// A value with no checkbox would be applied to SQL unseen, as with the default.
|
|
115
|
+
const unknown = preset.values.find((value) => !optionValues.has(value));
|
|
116
|
+
if (unknown !== undefined) {
|
|
117
|
+
return `names "${unknown}", which is not one of its options`;
|
|
118
|
+
}
|
|
119
|
+
labels.add(preset.label);
|
|
120
|
+
return null;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function usableFilterPresets(
|
|
125
|
+
presets: unknown,
|
|
126
|
+
optionValues: ReadonlySet<string>,
|
|
127
|
+
): FilterPreset[] {
|
|
128
|
+
if (!Array.isArray(presets)) return [];
|
|
129
|
+
const bounded = presets.slice(0, MAX_FILTER_PRESETS);
|
|
130
|
+
const issues = filterPresetIssues(bounded, optionValues);
|
|
131
|
+
return bounded.filter(
|
|
132
|
+
(preset, index): preset is FilterPreset => issues[index] === null,
|
|
64
133
|
);
|
|
65
134
|
}
|
|
66
135
|
|
|
@@ -38,12 +38,19 @@ export interface FilterOption {
|
|
|
38
38
|
label: string;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// A named selection of a multi-select filter's option values.
|
|
42
|
+
export interface FilterPreset {
|
|
43
|
+
label: string;
|
|
44
|
+
values: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
export interface DashboardFilter {
|
|
42
48
|
id: string;
|
|
43
49
|
label: string;
|
|
44
50
|
type: FilterType;
|
|
45
51
|
default?: string;
|
|
46
52
|
options?: FilterOption[];
|
|
53
|
+
presets?: FilterPreset[];
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
export type ColumnFormat =
|
|
@@ -3,14 +3,43 @@
|
|
|
3
3
|
All notable user-facing changes to Clips are documented here. Open it any time
|
|
4
4
|
from the command menu (Cmd+K → "What's new") or from Settings.
|
|
5
5
|
|
|
6
|
+
## 2026-10-15
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Clips stays labeled in the menu bar while idle
|
|
11
|
+
- The recording countdown starts when it appears and plays smoothly while Clips prepares a recording.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Rewind can be enabled from Include last without leaving the recorder.
|
|
16
|
+
- Screen Memory checks privacy exclusions during capture recovery
|
|
17
|
+
- Screen Memory retries capture promptly after audio mode changes fail
|
|
18
|
+
- Screen Memory recovers from capture interruptions promptly and preserves privacy-gap history.
|
|
19
|
+
- Earlier screen time keeps recording when audio capture starts or stops, and previews follow your selection.
|
|
20
|
+
|
|
6
21
|
## 2026-10-14
|
|
7
22
|
|
|
8
23
|
### Improved
|
|
9
24
|
|
|
25
|
+
- Earlier screen time now follows its clip's normal share rules: adding it no longer makes the clip private, and sharing the clip shares its earlier screen time with it.
|
|
26
|
+
- The recorder's Include last menu now reads None for no earlier time and has a Turn off item that stops Rewind, and the row reads Off once Rewind is stopped.
|
|
10
27
|
- Clips suggestions are shorter and match the active view.
|
|
11
28
|
|
|
12
29
|
### Fixed
|
|
13
30
|
|
|
31
|
+
- Clips keeps the camera preview open while Control Center is active.
|
|
32
|
+
- Clips recording errors without a recovery action now dismiss automatically
|
|
33
|
+
- Dictation cleanup can be undone and the original transcript remains available.
|
|
34
|
+
- Owners can download a local copy when a clip upload fails
|
|
35
|
+
- Opening a private clip you don't have access to now shows the request-access screen right away instead of a loading skeleton for several seconds.
|
|
36
|
+
- Restoring a dictation now stays final while cleanup is still running
|
|
37
|
+
- Selected-window recordings now include the camera bubble over the captured window.
|
|
38
|
+
- Shared Clips no longer show earlier screen time after a Clip expires or moves to Trash.
|
|
39
|
+
- Transcript edits stay intact when cleanup is running.
|
|
40
|
+
- Undo no longer removes a newer dictation cleanup
|
|
41
|
+
- Window recordings keep the camera preview inside the captured window.
|
|
42
|
+
- Window recordings keep the camera bubble visible and aligned with the selected window as it moves.
|
|
14
43
|
- Clips explains how to connect storage when saving screenshots or redacted clips.
|
|
15
44
|
- Clips hides unsupported Loom edits, keeps empty-state suggestions relevant, and prevents recording controls from covering Feedback.
|
|
16
45
|
- Clips no longer leave an upload progress toast running while waiting for storage setup.
|
|
@@ -19,6 +19,22 @@ const messages = {
|
|
|
19
19
|
turnOffRewind: "Turn off",
|
|
20
20
|
turnOffRewindFailed:
|
|
21
21
|
"Couldn't turn off Rewind. Stop your recording and try again.",
|
|
22
|
+
turnOnRewindFailed: "Couldn't turn on Rewind. Try again.",
|
|
23
|
+
turnOnRewindPermissionFailed:
|
|
24
|
+
"Allow screen recording for Clips in System Settings > Privacy & Security > Screen & System Audio Recording, then restart Clips and try again.",
|
|
25
|
+
turnOnRewindUnsupported:
|
|
26
|
+
"Rewind screen capture is available only on macOS.",
|
|
27
|
+
turnOnRewindNoSpace:
|
|
28
|
+
"There isn't enough free disk space to start Rewind. Free up space and try again.",
|
|
29
|
+
turnOnRewindStopClip: "Stop the current Clip before turning on Rewind.",
|
|
30
|
+
turnOnRewindStorageFailed:
|
|
31
|
+
"Couldn't access Rewind's storage. Check disk space and folder permissions, then try again.",
|
|
32
|
+
rewindConsentTitle: "Turn on Rewind?",
|
|
33
|
+
rewindConsentBody:
|
|
34
|
+
"Clips will continuously record your screen and keep it on this device. Agents get text excerpts, and video is shared only when you approve it.",
|
|
35
|
+
rewindConsentCancel: "Not now",
|
|
36
|
+
rewindConsentConfirm: "Turn on Rewind",
|
|
37
|
+
rewindConsentBusy: "Turning on…",
|
|
22
38
|
rewindOffTitle: "Turn on Rewind",
|
|
23
39
|
rewindOffBody:
|
|
24
40
|
"Off by default. Screen history stays on this device until you add it to a recording or approve a request to share it.",
|