@skillcdn/console 0.4.3 → 0.4.5
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/README.md +6 -6
- package/dist/api.d.ts +4 -4
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +4 -4
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +255 -33
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +20 -5
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +98 -26
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +39 -15
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plugin-check.d.ts +5 -1
- package/dist/cli/plugin-check.d.ts.map +1 -1
- package/dist/cli/plugin-check.js +23 -4
- package/dist/cli/plugin-check.js.map +1 -1
- package/dist/cli/plugin-cli.d.ts.map +1 -1
- package/dist/cli/plugin-cli.js +10 -1
- package/dist/cli/plugin-cli.js.map +1 -1
- package/dist/cli/plugin-init.d.ts +2 -0
- package/dist/cli/plugin-init.d.ts.map +1 -1
- package/dist/cli/plugin-init.js +6 -0
- package/dist/cli/plugin-init.js.map +1 -1
- package/dist/client.d.ts +32 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -2
- package/dist/client.js.map +1 -1
- package/dist/compat.js +1 -1
- package/dist/components/board.d.ts +8 -0
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +49 -18
- package/dist/components/board.js.map +1 -1
- package/dist/components/decision-form.d.ts +6 -3
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +24 -18
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +5 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +25 -5
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +34 -8
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/goals.d.ts.map +1 -1
- package/dist/components/goals.js +2 -2
- package/dist/components/goals.js.map +1 -1
- package/dist/components/home.d.ts +35 -0
- package/dist/components/home.d.ts.map +1 -0
- package/dist/components/home.js +326 -0
- package/dist/components/home.js.map +1 -0
- package/dist/components/my-decisions.d.ts +20 -3
- package/dist/components/my-decisions.d.ts.map +1 -1
- package/dist/components/my-decisions.js +14 -1
- package/dist/components/my-decisions.js.map +1 -1
- package/dist/components/pack-connect.d.ts.map +1 -1
- package/dist/components/pack-connect.js +5 -4
- package/dist/components/pack-connect.js.map +1 -1
- package/dist/components/people.d.ts +45 -1
- package/dist/components/people.d.ts.map +1 -1
- package/dist/components/people.js +125 -4
- package/dist/components/people.js.map +1 -1
- package/dist/components/picker.d.ts +8 -0
- package/dist/components/picker.d.ts.map +1 -1
- package/dist/components/picker.js +2 -2
- package/dist/components/picker.js.map +1 -1
- package/dist/components/plugin-frame.d.ts +3 -13
- package/dist/components/plugin-frame.d.ts.map +1 -1
- package/dist/components/plugin-frame.js +75 -21
- package/dist/components/plugin-frame.js.map +1 -1
- package/dist/components/plugin-settings.d.ts +5 -1
- package/dist/components/plugin-settings.d.ts.map +1 -1
- package/dist/components/plugin-settings.js +283 -45
- package/dist/components/plugin-settings.js.map +1 -1
- package/dist/components/projects.d.ts +0 -8
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +4 -14
- package/dist/components/projects.js.map +1 -1
- package/dist/components/searches.d.ts +11 -0
- package/dist/components/searches.d.ts.map +1 -0
- package/dist/components/searches.js +30 -0
- package/dist/components/searches.js.map +1 -0
- package/dist/components/task-form.d.ts.map +1 -1
- package/dist/components/task-form.js +6 -5
- package/dist/components/task-form.js.map +1 -1
- package/dist/components/task-view.d.ts.map +1 -1
- package/dist/components/task-view.js +2 -1
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/ui.d.ts +16 -0
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +15 -2
- package/dist/components/ui.js.map +1 -1
- package/dist/connect-page.d.ts.map +1 -1
- package/dist/connect-page.js +5 -6
- package/dist/connect-page.js.map +1 -1
- package/dist/console.css +1123 -258
- package/dist/console.d.ts +12 -4
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +123 -27
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +18 -3
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +41 -1
- package/dist/data.js.map +1 -1
- package/dist/docs-page.d.ts.map +1 -1
- package/dist/docs-page.js +6 -5
- package/dist/docs-page.js.map +1 -1
- package/dist/home-tiles.d.ts +92 -0
- package/dist/home-tiles.d.ts.map +1 -0
- package/dist/home-tiles.js +294 -0
- package/dist/home-tiles.js.map +1 -0
- package/dist/i18n/en.d.ts +264 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +260 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/index.d.ts +4 -2
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +10 -11
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +255 -4
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +12 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +12 -0
- package/dist/limits.js.map +1 -1
- package/dist/plugin/args.d.ts +6 -1
- package/dist/plugin/args.d.ts.map +1 -1
- package/dist/plugin/args.js +16 -11
- package/dist/plugin/args.js.map +1 -1
- package/dist/plugin/bridge.d.ts +63 -3
- package/dist/plugin/bridge.d.ts.map +1 -1
- package/dist/plugin/bridge.js +54 -0
- package/dist/plugin/bridge.js.map +1 -1
- package/dist/plugin/capabilities.d.ts +7 -1
- package/dist/plugin/capabilities.d.ts.map +1 -1
- package/dist/plugin/capabilities.js +24 -1
- package/dist/plugin/capabilities.js.map +1 -1
- package/dist/plugin/context.d.ts +174 -12
- package/dist/plugin/context.d.ts.map +1 -1
- package/dist/plugin/define.d.ts +14 -1
- package/dist/plugin/define.d.ts.map +1 -1
- package/dist/plugin/define.js +18 -2
- package/dist/plugin/define.js.map +1 -1
- package/dist/plugin/limits.d.ts +9 -2
- package/dist/plugin/limits.d.ts.map +1 -1
- package/dist/plugin/limits.js +9 -2
- package/dist/plugin/limits.js.map +1 -1
- package/dist/plugin/manifest.d.ts +98 -6
- package/dist/plugin/manifest.d.ts.map +1 -1
- package/dist/plugin/manifest.js +171 -10
- package/dist/plugin/manifest.js.map +1 -1
- package/dist/plugin/rest-schemas.d.ts +120 -0
- package/dist/plugin/rest-schemas.d.ts.map +1 -1
- package/dist/plugin/rest-schemas.js +78 -5
- package/dist/plugin/rest-schemas.js.map +1 -1
- package/dist/plugin/sandbox.d.ts +4 -3
- package/dist/plugin/sandbox.d.ts.map +1 -1
- package/dist/plugin/sandbox.js +33 -6
- package/dist/plugin/sandbox.js.map +1 -1
- package/dist/plugin/semver.d.ts +26 -0
- package/dist/plugin/semver.d.ts.map +1 -0
- package/dist/plugin/semver.js +76 -0
- package/dist/plugin/semver.js.map +1 -0
- package/dist/plugin/testing.d.ts +15 -1
- package/dist/plugin/testing.d.ts.map +1 -1
- package/dist/plugin/testing.js +42 -5
- package/dist/plugin/testing.js.map +1 -1
- package/dist/plugin/web.d.ts +16 -1
- package/dist/plugin/web.d.ts.map +1 -1
- package/dist/plugin/web.js +44 -1
- package/dist/plugin/web.js.map +1 -1
- package/dist/plugin.d.ts +3 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +3 -2
- package/dist/plugin.js.map +1 -1
- package/dist/router.d.ts +19 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +18 -0
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +11 -2
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +14 -1
- package/dist/routes.js.map +1 -1
- package/dist/rules.d.ts +2 -2
- package/dist/rules.js +3 -3
- package/dist/schemas.d.ts +864 -2
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +156 -9
- package/dist/schemas.js.map +1 -1
- package/dist/use-action.d.ts +8 -2
- package/dist/use-action.d.ts.map +1 -1
- package/dist/use-action.js +13 -6
- package/dist/use-action.js.map +1 -1
- package/dist/vocabulary.d.ts +3 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +9 -0
- package/dist/vocabulary.js.map +1 -1
- package/docs/specs/cli.md +10 -6
- package/docs/specs/plugins.md +83 -62
- package/docs/specs/rest.md +34 -16
- package/package.json +1 -1
- package/skills/building-a-plugin/SKILL.md +15 -12
- package/skills/planning-the-board/references/setting-up.md +2 -1
- package/skills/working-the-board/SKILL.md +3 -3
- package/src/api.ts +23 -0
- package/src/cli/cli.ts +323 -30
- package/src/cli/format.ts +122 -26
- package/src/cli/help.ts +43 -14
- package/src/cli/plugin-check.ts +41 -2
- package/src/cli/plugin-cli.ts +10 -1
- package/src/cli/plugin-init.ts +8 -0
- package/src/client.ts +78 -0
- package/src/compat.ts +1 -1
- package/src/components/board.tsx +86 -45
- package/src/components/decision-form.tsx +73 -36
- package/src/components/decision-list.tsx +45 -13
- package/src/components/event-feed.tsx +40 -7
- package/src/components/goals.tsx +4 -1
- package/src/components/home.tsx +615 -0
- package/src/components/my-decisions.tsx +50 -5
- package/src/components/pack-connect.tsx +5 -4
- package/src/components/people.tsx +500 -61
- package/src/components/picker.tsx +11 -2
- package/src/components/plugin-frame.tsx +85 -63
- package/src/components/plugin-settings.tsx +735 -185
- package/src/components/projects.tsx +14 -69
- package/src/components/searches.ts +47 -0
- package/src/components/task-form.tsx +23 -28
- package/src/components/task-view.tsx +7 -0
- package/src/components/ui.tsx +42 -2
- package/src/connect-page.tsx +5 -6
- package/src/console.tsx +303 -64
- package/src/data.ts +62 -3
- package/src/docs-page.tsx +6 -5
- package/src/home-tiles.ts +438 -0
- package/src/i18n/en.ts +274 -0
- package/src/i18n/index.ts +17 -14
- package/src/i18n/ko.ts +267 -4
- package/src/index.ts +18 -5
- package/src/limits.ts +12 -0
- package/src/plugin/args.ts +16 -11
- package/src/plugin/bridge.ts +122 -1
- package/src/plugin/capabilities.ts +28 -1
- package/src/plugin/context.ts +186 -9
- package/src/plugin/define.ts +38 -1
- package/src/plugin/limits.ts +9 -2
- package/src/plugin/manifest.ts +228 -13
- package/src/plugin/rest-schemas.ts +89 -6
- package/src/plugin/sandbox.ts +36 -8
- package/src/plugin/semver.ts +89 -0
- package/src/plugin/testing.ts +63 -6
- package/src/plugin/web.ts +66 -0
- package/src/plugin.ts +19 -0
- package/src/router.ts +25 -0
- package/src/routes.ts +17 -2
- package/src/rules.ts +3 -3
- package/src/schemas.ts +197 -10
- package/src/styles/console.css +1123 -258
- package/src/use-action.ts +16 -7
- package/src/vocabulary.ts +10 -0
- package/template/plugin/console-plugin.json +3 -1
|
@@ -62,10 +62,18 @@ export interface PickerProps<T> {
|
|
|
62
62
|
readonly onChange: (item: T | undefined) => void;
|
|
63
63
|
/** The field's name, for the input and the list. */
|
|
64
64
|
readonly label: string;
|
|
65
|
+
/** The input's id, where a label points at it. */
|
|
66
|
+
readonly id?: string | undefined;
|
|
65
67
|
readonly placeholder: string;
|
|
66
68
|
readonly disabled?: boolean | undefined;
|
|
67
69
|
/** What the search may answer with but cannot be chosen: a task, as its own parent. */
|
|
68
70
|
readonly exclude?: ((item: T) => boolean) | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* A class of the picker's own beside `sc-picker`: `sc-picker-quiet`, a picker that reads as
|
|
73
|
+
* what it holds until the pointer or the focus is on it, a fact changed in place; or
|
|
74
|
+
* `sc-picker-add`, the way to add one more at the end of a row of chips, as low as a chip.
|
|
75
|
+
*/
|
|
76
|
+
readonly className?: string | undefined;
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
export function Picker<T>(props: PickerProps<T>) {
|
|
@@ -204,7 +212,7 @@ export function Picker<T>(props: PickerProps<T>) {
|
|
|
204
212
|
|
|
205
213
|
if (props.value !== undefined || props.held !== undefined) {
|
|
206
214
|
return (
|
|
207
|
-
<div className="sc-picker sc-picker-chosen">
|
|
215
|
+
<div className={cx("sc-picker", "sc-picker-chosen", props.className)}>
|
|
208
216
|
<span className="sc-picker-value">
|
|
209
217
|
{props.value === undefined ? props.held : props.render(props.value)}
|
|
210
218
|
</span>
|
|
@@ -222,9 +230,10 @@ export function Picker<T>(props: PickerProps<T>) {
|
|
|
222
230
|
}
|
|
223
231
|
|
|
224
232
|
return (
|
|
225
|
-
<div className="sc-picker">
|
|
233
|
+
<div className={cx("sc-picker", props.className)}>
|
|
226
234
|
<input
|
|
227
235
|
ref={field}
|
|
236
|
+
id={props.id}
|
|
228
237
|
className="sc-input"
|
|
229
238
|
type="text"
|
|
230
239
|
role="combobox"
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import type { ConsoleClient, PluginClient } from "../client.js";
|
|
3
|
+
import { firstDayOfWeek } from "../dates.js";
|
|
4
|
+
import type { Messages } from "../i18n/en.js";
|
|
5
|
+
import { useWords } from "../i18n/index.js";
|
|
6
|
+
import type { PluginWidgetSizeName } from "../plugin/bridge.js";
|
|
3
7
|
import {
|
|
4
8
|
type BridgeCall,
|
|
5
9
|
type BridgeEventKind,
|
|
6
10
|
type BridgeToFrame,
|
|
7
11
|
isBridgePath,
|
|
8
12
|
isBridgeToPage,
|
|
13
|
+
isOpenableFrom,
|
|
9
14
|
PLUGIN_BRIDGE_VERSION,
|
|
10
15
|
} from "../plugin/bridge.js";
|
|
11
|
-
import { MAX_PLUGIN_WIDGET_HEIGHT } from "../plugin/limits.js";
|
|
12
16
|
import type { Navigation } from "../router.js";
|
|
13
17
|
import type { RestEvent, RestMe, RestPlugin, RestProject } from "../schemas.js";
|
|
14
18
|
import { CONSOLE_TOKENS } from "../tokens.js";
|
|
19
|
+
import { wordsForFailure } from "../use-action.js";
|
|
20
|
+
import { CONSOLE_LANGUAGES } from "../vocabulary.js";
|
|
21
|
+
import { weekStartOf } from "./workspace.js";
|
|
15
22
|
|
|
16
23
|
// A plugin's page or widget on the console's page (ADR-0054, docs/specs/plugins.md, "Pages"):
|
|
17
24
|
// a sandboxed frame whose document the console serves, and the console's side of the bridge.
|
|
@@ -29,6 +36,8 @@ export interface PluginFrameProps {
|
|
|
29
36
|
/** The page shown, or the widget; one of the two. */
|
|
30
37
|
readonly page?: string | undefined;
|
|
31
38
|
readonly widget?: string | undefined;
|
|
39
|
+
/** The size the widget has (ADR-0059), told to the frame at the start and when it changes. */
|
|
40
|
+
readonly size?: PluginWidgetSizeName | undefined;
|
|
32
41
|
/** The project the page is on, or nothing on the workspace. */
|
|
33
42
|
readonly project?: RestProject | undefined;
|
|
34
43
|
/** What follows the page in the address, handed to the frame. */
|
|
@@ -127,6 +136,12 @@ export async function performBridgeCall(
|
|
|
127
136
|
transfer: [bytes],
|
|
128
137
|
};
|
|
129
138
|
}
|
|
139
|
+
case "people.picture": {
|
|
140
|
+
const id = text(params.id, "id");
|
|
141
|
+
const picture = await plugin.personPicture(id);
|
|
142
|
+
const bytes = new Uint8Array(picture.bytes).buffer;
|
|
143
|
+
return { value: { type: picture.contentType, bytes }, transfer: [bytes] };
|
|
144
|
+
}
|
|
130
145
|
case "files.put": {
|
|
131
146
|
const name = text(params.name, "name");
|
|
132
147
|
const type = text(params.type, "type");
|
|
@@ -146,6 +161,20 @@ export async function performBridgeCall(
|
|
|
146
161
|
}
|
|
147
162
|
|
|
148
163
|
/** The console's computed design tokens, as the page's root has them: what a frame is told to look like. */
|
|
164
|
+
/**
|
|
165
|
+
* Whether a person pressed something a moment ago, as the browser keeps it: a link in a frame
|
|
166
|
+
* gives the page this too, which is what lets the page open a tab for it; a browser without the
|
|
167
|
+
* API is left to decide for itself, as it does for any page.
|
|
168
|
+
*/
|
|
169
|
+
function recentlyPressed(): boolean {
|
|
170
|
+
if (typeof navigator === "undefined") {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
const activation = (navigator as Navigator & { userActivation?: { isActive: boolean } })
|
|
174
|
+
.userActivation;
|
|
175
|
+
return activation === undefined || activation.isActive;
|
|
176
|
+
}
|
|
177
|
+
|
|
149
178
|
export function readTheme(): Record<string, string> {
|
|
150
179
|
if (typeof document === "undefined" || typeof getComputedStyle === "undefined") {
|
|
151
180
|
return {};
|
|
@@ -161,8 +190,14 @@ export function readTheme(): Record<string, string> {
|
|
|
161
190
|
return theme;
|
|
162
191
|
}
|
|
163
192
|
|
|
164
|
-
/**
|
|
165
|
-
|
|
193
|
+
/**
|
|
194
|
+
* What a failure of a call says to the frame: the code, with the console's own refusals in the
|
|
195
|
+
* person's language, a plugin's own in its own words; or the console's own failure.
|
|
196
|
+
*/
|
|
197
|
+
function errorOf(
|
|
198
|
+
error: unknown,
|
|
199
|
+
pack: Messages,
|
|
200
|
+
): { readonly code: string; readonly message: string } {
|
|
166
201
|
if (error instanceof BridgeRefusal) {
|
|
167
202
|
return { code: error.code, message: error.message };
|
|
168
203
|
}
|
|
@@ -174,16 +209,20 @@ function errorOf(error: unknown): { readonly code: string; readonly message: str
|
|
|
174
209
|
"message" in error &&
|
|
175
210
|
typeof error.message === "string"
|
|
176
211
|
) {
|
|
177
|
-
return { code: error.code, message: error.message };
|
|
212
|
+
return { code: error.code, message: wordsForFailure(error, pack) ?? error.message };
|
|
178
213
|
}
|
|
179
214
|
return { code: "plugin.failed", message: "the call failed" };
|
|
180
215
|
}
|
|
181
216
|
|
|
182
217
|
export function PluginFrame(props: PluginFrameProps) {
|
|
183
218
|
const { plugin, page, widget, project, me, language, client, navigation, events } = props;
|
|
219
|
+
const pack = useWords();
|
|
220
|
+
const words = pack.plugins;
|
|
184
221
|
const frame = useRef<HTMLIFrameElement>(null);
|
|
185
222
|
const isWidget = widget !== undefined;
|
|
186
|
-
const [height, setHeight] = useState(
|
|
223
|
+
const [height, setHeight] = useState(480);
|
|
224
|
+
/** Whether the console served the frame nothing to show: a plugin turned off since the page read it, say. */
|
|
225
|
+
const [unavailable, setUnavailable] = useState(false);
|
|
187
226
|
const subscribed = useRef<Set<BridgeEventKind>>(new Set());
|
|
188
227
|
const ready = useRef(false);
|
|
189
228
|
const lastPluginEvent = useRef(0);
|
|
@@ -199,6 +238,8 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
199
238
|
}),
|
|
200
239
|
[scoped, page, widget, project],
|
|
201
240
|
);
|
|
241
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: another address is another document
|
|
242
|
+
useEffect(() => setUnavailable(false), [src]);
|
|
202
243
|
|
|
203
244
|
const post = useCallback((message: BridgeToFrame, transfer?: Transferable[]) => {
|
|
204
245
|
// The frame's origin is opaque, so the target is any: the frame itself checks the sender.
|
|
@@ -220,6 +261,9 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
220
261
|
}
|
|
221
262
|
const message = event.data;
|
|
222
263
|
switch (message.t) {
|
|
264
|
+
case "unavailable":
|
|
265
|
+
setUnavailable(true);
|
|
266
|
+
return;
|
|
223
267
|
case "ready":
|
|
224
268
|
ready.current = true;
|
|
225
269
|
post({
|
|
@@ -228,6 +272,7 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
228
272
|
plugin: { key: plugin.key, version: plugin.version },
|
|
229
273
|
...(page === undefined ? {} : { page }),
|
|
230
274
|
...(widget === undefined ? {} : { widget }),
|
|
275
|
+
...(props.size === undefined ? {} : { size: props.size }),
|
|
231
276
|
project: project === undefined ? null : { key: project.key, name: project.name },
|
|
232
277
|
person: {
|
|
233
278
|
id: me.person?.id ?? "",
|
|
@@ -236,6 +281,13 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
236
281
|
language,
|
|
237
282
|
admin: me.person?.role === "admin",
|
|
238
283
|
},
|
|
284
|
+
workspace: {
|
|
285
|
+
name: me.workspace.name,
|
|
286
|
+
languages: [...CONSOLE_LANGUAGES],
|
|
287
|
+
timeZone: me.workspace.timeZone ?? "UTC",
|
|
288
|
+
weekStart: weekStartOf(me.workspace),
|
|
289
|
+
firstDay: firstDayOfWeek(language, weekStartOf(me.workspace)),
|
|
290
|
+
},
|
|
239
291
|
roles: plugin.roles,
|
|
240
292
|
path,
|
|
241
293
|
language,
|
|
@@ -256,7 +308,7 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
256
308
|
t: "result",
|
|
257
309
|
id: message.id,
|
|
258
310
|
ok: false,
|
|
259
|
-
error: errorOf(error),
|
|
311
|
+
error: errorOf(error, pack),
|
|
260
312
|
}),
|
|
261
313
|
);
|
|
262
314
|
return;
|
|
@@ -266,15 +318,10 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
266
318
|
}
|
|
267
319
|
return;
|
|
268
320
|
case "resize":
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
Math.ceil(message.height),
|
|
274
|
-
isWidget ? MAX_PLUGIN_WIDGET_HEIGHT : MAX_PAGE_HEIGHT,
|
|
275
|
-
),
|
|
276
|
-
),
|
|
277
|
-
);
|
|
321
|
+
// A widget fills the cells its size gives it (ADR-0059); a page is as tall as it asks.
|
|
322
|
+
if (!isWidget) {
|
|
323
|
+
setHeight(Math.max(40, Math.min(Math.ceil(message.height), MAX_PAGE_HEIGHT)));
|
|
324
|
+
}
|
|
278
325
|
return;
|
|
279
326
|
case "title":
|
|
280
327
|
if (!isWidget && typeof document !== "undefined") {
|
|
@@ -289,6 +336,14 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
289
336
|
anchor.click();
|
|
290
337
|
return;
|
|
291
338
|
}
|
|
339
|
+
case "open":
|
|
340
|
+
// A link a person pressed in the frame (ADR-0067): a web or a mail address, opened in a
|
|
341
|
+
// new tab with no opener and no referrer, and only while the press is recent, which a
|
|
342
|
+
// press in the frame gives this page too.
|
|
343
|
+
if (isOpenableFrom(message.url, window.location.origin) && recentlyPressed()) {
|
|
344
|
+
window.open(message.url, "_blank", "noopener,noreferrer");
|
|
345
|
+
}
|
|
346
|
+
return;
|
|
292
347
|
case "subscribe":
|
|
293
348
|
subscribed.current = new Set(message.kinds);
|
|
294
349
|
return;
|
|
@@ -315,6 +370,14 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
315
370
|
}
|
|
316
371
|
}, [path, post]);
|
|
317
372
|
|
|
373
|
+
// The person gave the widget another size: the frame lays itself out for it.
|
|
374
|
+
const size = props.size;
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
if (ready.current && size !== undefined) {
|
|
377
|
+
post({ v: PLUGIN_BRIDGE_VERSION, t: "size", size });
|
|
378
|
+
}
|
|
379
|
+
}, [size, post]);
|
|
380
|
+
|
|
318
381
|
// The plugin's commands, as the project's feed tells of them: a frame that listens hears of each.
|
|
319
382
|
useEffect(() => {
|
|
320
383
|
if (events === undefined) {
|
|
@@ -375,63 +438,22 @@ export function PluginFrame(props: PluginFrameProps) {
|
|
|
375
438
|
.catch(() => undefined);
|
|
376
439
|
}, [scoped]);
|
|
377
440
|
|
|
441
|
+
if (unavailable) {
|
|
442
|
+
return <p className="sc-plugin-frame-unavailable">{words.frameUnavailable}</p>;
|
|
443
|
+
}
|
|
378
444
|
return (
|
|
379
445
|
<iframe
|
|
380
446
|
ref={frame}
|
|
381
|
-
className={
|
|
447
|
+
className={
|
|
448
|
+
isWidget ? "sc-plugin-frame sc-plugin-frame-widget" : "sc-plugin-frame sc-plugin-frame-page"
|
|
449
|
+
}
|
|
382
450
|
src={src}
|
|
383
451
|
title={props.title}
|
|
384
452
|
// As the document's own policy says: scripts, and forms firing their submit event, which
|
|
385
453
|
// the policy's form-action 'none' keeps from going anywhere.
|
|
386
454
|
sandbox="allow-scripts allow-forms"
|
|
387
455
|
referrerPolicy="origin"
|
|
388
|
-
style={{ height: `${height}px` }}
|
|
456
|
+
style={isWidget ? undefined : { height: `${height}px` }}
|
|
389
457
|
/>
|
|
390
458
|
);
|
|
391
459
|
}
|
|
392
|
-
|
|
393
|
-
export interface PluginWidgetsProps {
|
|
394
|
-
readonly plugins: readonly RestPlugin[];
|
|
395
|
-
readonly place: "home:top" | "home:bottom";
|
|
396
|
-
readonly me: RestMe;
|
|
397
|
-
readonly language: string;
|
|
398
|
-
readonly client: ConsoleClient;
|
|
399
|
-
readonly navigation: Navigation;
|
|
400
|
-
/** The widget's name in the person's language. */
|
|
401
|
-
readonly nameOf: (name: Readonly<Record<string, string>>) => string;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/** The widgets at a place of the front page, in a grid of six columns: a third takes two, a half three, the full six. */
|
|
405
|
-
export function PluginWidgets(props: PluginWidgetsProps) {
|
|
406
|
-
const shown = props.plugins.flatMap((plugin) =>
|
|
407
|
-
plugin.widgets
|
|
408
|
-
.filter((widget) => widget.place === props.place)
|
|
409
|
-
.map((widget) => ({ plugin, widget })),
|
|
410
|
-
);
|
|
411
|
-
if (shown.length === 0) {
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
return (
|
|
415
|
-
<section
|
|
416
|
-
className={`sc-widgets ${props.place === "home:top" ? "sc-widgets-top" : "sc-widgets-bottom"}`}
|
|
417
|
-
>
|
|
418
|
-
{shown.map(({ plugin, widget }) => (
|
|
419
|
-
<article
|
|
420
|
-
key={`${plugin.key}:${widget.key}`}
|
|
421
|
-
className={`sc-widget sc-widget-${widget.size}`}
|
|
422
|
-
>
|
|
423
|
-
<h2 className="sc-widget-title">{props.nameOf(widget.name)}</h2>
|
|
424
|
-
<PluginFrame
|
|
425
|
-
plugin={plugin}
|
|
426
|
-
widget={widget.key}
|
|
427
|
-
me={props.me}
|
|
428
|
-
language={props.language}
|
|
429
|
-
client={props.client}
|
|
430
|
-
navigation={props.navigation}
|
|
431
|
-
title={props.nameOf(widget.name)}
|
|
432
|
-
/>
|
|
433
|
-
</article>
|
|
434
|
-
))}
|
|
435
|
-
</section>
|
|
436
|
-
);
|
|
437
|
-
}
|