@maple-kit/ui 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/composer/attachments.js +14 -9
- package/dist/composer/badge.js +2 -2
- package/dist/composer/css.js +38 -0
- package/dist/island/css.js +89 -0
- package/dist/island/index.d.ts +2 -1
- package/dist/island/index.js +2 -1
- package/dist/island/language.d.ts +3 -0
- package/dist/island/language.js +27 -2
- package/dist/island/list.js +3 -2
- package/dist/island/transfer.d.ts +16 -0
- package/dist/island/transfer.js +116 -0
- package/dist/island/unsent.js +23 -3
- package/dist/maple.js +2 -1
- package/dist/marks/layer.js +15 -4
- package/dist/marks/placement.d.ts +3 -1
- package/dist/marks/placement.js +9 -3
- package/dist/marks/ring.d.ts +6 -1
- package/dist/marks/ring.js +14 -7
- package/dist/marks/route.js +37 -0
- package/dist/mock/language.d.ts +2 -0
- package/dist/mock/language.js +2 -0
- package/dist/mock/layers.js +6 -3
- package/dist/mock/mock.js +5 -1
- package/dist/notice/index.d.ts +2 -2
- package/dist/notice/index.js +2 -2
- package/dist/notice/language.d.ts +4 -1
- package/dist/notice/language.js +4 -1
- package/dist/notice/notice.js +2 -2
- package/dist/picker/target.js +4 -2
- package/package.json +4 -4
|
@@ -83,28 +83,33 @@ function filled(scope, held) {
|
|
|
83
83
|
src: scope.pending?.preview.url,
|
|
84
84
|
alt: ATTACH_WORDS.alt
|
|
85
85
|
}),
|
|
86
|
-
|
|
86
|
+
createElement("span", {
|
|
87
|
+
key: "body",
|
|
88
|
+
className: "mk-shot-body"
|
|
89
|
+
}, said(taking(held), captured && keeps, "said", "mk-shot-said mk-shot-line"), failed ? createElement("span", {
|
|
90
|
+
key: "bad",
|
|
91
|
+
className: "mk-chip"
|
|
92
|
+
}, ATTACH_WORDS.failed) : null),
|
|
87
93
|
createElement("button", {
|
|
88
94
|
key: "off",
|
|
89
95
|
type: "button",
|
|
90
96
|
className: "mk-btn mk-btn-quiet mk-press",
|
|
91
97
|
onClick: scope.clear
|
|
92
|
-
}, ATTACH_WORDS.remove)
|
|
93
|
-
failed ? createElement("span", {
|
|
94
|
-
key: "bad",
|
|
95
|
-
className: "mk-chip"
|
|
96
|
-
}, ATTACH_WORDS.failed) : null
|
|
98
|
+
}, ATTACH_WORDS.remove)
|
|
97
99
|
];
|
|
98
100
|
}
|
|
99
|
-
function said(words, byMaple, key) {
|
|
101
|
+
function said(words, byMaple, key, className = "mk-shot-said") {
|
|
100
102
|
return createElement("span", {
|
|
101
103
|
key,
|
|
102
|
-
className
|
|
104
|
+
className,
|
|
103
105
|
...byMaple ? { "data-mk-maple": "true" } : {}
|
|
104
106
|
}, byMaple ? createElement(SparkleIcon, {
|
|
105
107
|
key: "spark",
|
|
106
108
|
size: 12
|
|
107
|
-
}) : null,
|
|
109
|
+
}) : null, createElement("span", {
|
|
110
|
+
key: "words",
|
|
111
|
+
className: "mk-shot-words"
|
|
112
|
+
}, words));
|
|
108
113
|
}
|
|
109
114
|
function taking(held) {
|
|
110
115
|
if (!held.keeps) return ATTACH_WORDS.notKept;
|
package/dist/composer/badge.js
CHANGED
|
@@ -8,12 +8,12 @@ const CONTEXT_LABELS = {
|
|
|
8
8
|
open: "Hide what was captured"
|
|
9
9
|
};
|
|
10
10
|
const MapleContextBadge = forwardRef(function MapleContextBadge(props, ref) {
|
|
11
|
-
const { composer
|
|
11
|
+
const { composer } = useMaple();
|
|
12
12
|
const client = useMapleClient();
|
|
13
13
|
const context = props.context ?? composer.target?.context;
|
|
14
14
|
const Element = props.asChild ? Slot : "section";
|
|
15
15
|
if (!context) return null;
|
|
16
|
-
const rows = contextRows(context
|
|
16
|
+
const rows = contextRows(context);
|
|
17
17
|
const collapsible = props.collapsible !== false && composer.viewing === void 0;
|
|
18
18
|
const open = !collapsible || composer.contextOpen;
|
|
19
19
|
return createElement(Element, {
|
package/dist/composer/css.js
CHANGED
|
@@ -239,6 +239,42 @@ function scoreCard() {
|
|
|
239
239
|
background: var(--mk-line);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
`.trim();
|
|
243
|
+
}
|
|
244
|
+
function shotBodyCss() {
|
|
245
|
+
return `
|
|
246
|
+
/* The held image's caption and its failure share one column between the
|
|
247
|
+
thumbnail and Remove, so a failure adds a line rather than a layout. */
|
|
248
|
+
.mk-shot-body {
|
|
249
|
+
flex: 1 1 auto;
|
|
250
|
+
min-width: 0;
|
|
251
|
+
display: flex;
|
|
252
|
+
flex-direction: column;
|
|
253
|
+
align-items: flex-start;
|
|
254
|
+
gap: 4px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.mk-shot-body .mk-shot-said {
|
|
258
|
+
flex: none;
|
|
259
|
+
max-width: 100%;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.mk-shot-line .mk-shot-words {
|
|
263
|
+
min-width: 0;
|
|
264
|
+
overflow: hidden;
|
|
265
|
+
white-space: nowrap;
|
|
266
|
+
text-overflow: ellipsis;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.mk-shot-body .mk-chip {
|
|
270
|
+
max-width: 100%;
|
|
271
|
+
white-space: normal;
|
|
272
|
+
border-radius: var(--mk-r-xs);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.mk-shots > .mk-btn {
|
|
276
|
+
flex: none;
|
|
277
|
+
}
|
|
242
278
|
`.trim();
|
|
243
279
|
}
|
|
244
280
|
function kindChip() {
|
|
@@ -561,6 +597,8 @@ ${scoreCard()}
|
|
|
561
597
|
|
|
562
598
|
${kindChip()}
|
|
563
599
|
|
|
600
|
+
${shotBodyCss()}
|
|
601
|
+
|
|
564
602
|
.mk-leave {
|
|
565
603
|
position: absolute;
|
|
566
604
|
top: 12px;
|
package/dist/island/css.js
CHANGED
|
@@ -12,6 +12,7 @@ function islandCss() {
|
|
|
12
12
|
rowDetail(),
|
|
13
13
|
developer(),
|
|
14
14
|
ledger(),
|
|
15
|
+
transfer(),
|
|
15
16
|
newComment(),
|
|
16
17
|
keyframes(),
|
|
17
18
|
sheet()
|
|
@@ -936,6 +937,78 @@ function developer() {
|
|
|
936
937
|
|
|
937
938
|
`.trim();
|
|
938
939
|
}
|
|
940
|
+
function transfer() {
|
|
941
|
+
return `
|
|
942
|
+
.mk-transfer,
|
|
943
|
+
.mk-transfer-other {
|
|
944
|
+
flex: none;
|
|
945
|
+
display: flex;
|
|
946
|
+
flex-wrap: wrap;
|
|
947
|
+
align-items: center;
|
|
948
|
+
gap: 6px 8px;
|
|
949
|
+
padding: 6px 10px;
|
|
950
|
+
border-top: 1px solid var(--mk-line);
|
|
951
|
+
background: var(--mk-sunk);
|
|
952
|
+
color: var(--mk-faint);
|
|
953
|
+
font-size: 11px;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.mk-transfer-said {
|
|
957
|
+
flex: 1 1 auto;
|
|
958
|
+
min-width: 0;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.mk-transfer-said code {
|
|
962
|
+
color: var(--mk-muted);
|
|
963
|
+
font-family: inherit;
|
|
964
|
+
font-weight: 600;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.mk-transfer-panel,
|
|
968
|
+
.mk-transfer-note {
|
|
969
|
+
flex: 1 1 100%;
|
|
970
|
+
margin: 0;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.mk-transfer-panel {
|
|
974
|
+
display: flex;
|
|
975
|
+
flex-direction: column;
|
|
976
|
+
gap: 6px;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.mk-transfer-box {
|
|
980
|
+
box-sizing: border-box;
|
|
981
|
+
width: 100%;
|
|
982
|
+
resize: vertical;
|
|
983
|
+
padding: 6px 8px;
|
|
984
|
+
border: 1px solid var(--mk-line);
|
|
985
|
+
border-radius: 6px;
|
|
986
|
+
background: var(--mk-bg);
|
|
987
|
+
color: var(--mk-fg);
|
|
988
|
+
font: inherit;
|
|
989
|
+
font-size: 11px;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.mk-transfer-row,
|
|
993
|
+
.mk-transfer-ask {
|
|
994
|
+
display: flex;
|
|
995
|
+
align-items: center;
|
|
996
|
+
gap: 8px;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.mk-transfer-file {
|
|
1000
|
+
flex: 1 1 auto;
|
|
1001
|
+
min-width: 0;
|
|
1002
|
+
color: var(--mk-muted);
|
|
1003
|
+
font-size: 11px;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.mk-unsent-copy:disabled {
|
|
1007
|
+
opacity: 0.5;
|
|
1008
|
+
cursor: default;
|
|
1009
|
+
}
|
|
1010
|
+
`;
|
|
1011
|
+
}
|
|
939
1012
|
function ledger() {
|
|
940
1013
|
return `
|
|
941
1014
|
.mk-unsent {
|
|
@@ -1017,6 +1090,22 @@ function ledger() {
|
|
|
1017
1090
|
cursor: pointer;
|
|
1018
1091
|
}
|
|
1019
1092
|
|
|
1093
|
+
.mk-unsent-where {
|
|
1094
|
+
flex: none;
|
|
1095
|
+
max-width: 45%;
|
|
1096
|
+
color: var(--mk-faint);
|
|
1097
|
+
font-size: 11px;
|
|
1098
|
+
text-decoration: underline;
|
|
1099
|
+
text-underline-offset: 2px;
|
|
1100
|
+
white-space: nowrap;
|
|
1101
|
+
overflow: hidden;
|
|
1102
|
+
text-overflow: ellipsis;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
.mk-unsent-where:hover {
|
|
1106
|
+
color: var(--mk-fg);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1020
1109
|
.mk-unsent-drop {
|
|
1021
1110
|
flex: none;
|
|
1022
1111
|
padding: 2px 7px;
|
package/dist/island/index.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ import { NewComment, NewCommentProps, PickButton, PickButtonProps } from "./new-
|
|
|
15
15
|
import { Settings, SettingsProps } from "./settings.js";
|
|
16
16
|
import { STAGGER_ROWS } from "./stagger.js";
|
|
17
17
|
import { absoluteTime, relativeTime } from "./time.js";
|
|
18
|
+
import { ImportDrafts, ImportDraftsProps, OtherDrafts, OtherDraftsProps } from "./transfer.js";
|
|
18
19
|
import { IslandTrigger, IslandTriggerProps } from "./trigger.js";
|
|
19
20
|
import { Unsent, UnsentProps } from "./unsent.js";
|
|
20
21
|
import { WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, WordmarkProps } from "./wordmark.js";
|
|
21
|
-
export { APPROVE_COPY, Account, type AccountProps, Approve, type ApproveProps, Branch, type BranchProps, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, type DragHandlers, FILTER_LABELS, type FilterCounts, Filters, type FiltersProps, Header, type HeaderProps, ISLAND_COPY, Island, IslandContent, type IslandContentProps, IslandContextError, type IslandContextValue, type IslandPhase, type IslandProps, IslandTrigger, type IslandTriggerProps, Item, type ItemProps, List, type ListProps, Logo, type LogoProps, NewComment, type NewCommentProps, PickButton, type PickButtonProps, type PointerEventLike, SETTINGS_COPY, STAGGER_ROWS, Settings, type SettingsProps, TIME_DELAY_MS, UNSENT_COPY, Unsent, type UnsentProps, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, type WordmarkProps, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
|
|
22
|
+
export { APPROVE_COPY, Account, type AccountProps, Approve, type ApproveProps, Branch, type BranchProps, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, type DragHandlers, FILTER_LABELS, type FilterCounts, Filters, type FiltersProps, Header, type HeaderProps, ISLAND_COPY, ImportDrafts, type ImportDraftsProps, Island, IslandContent, type IslandContentProps, IslandContextError, type IslandContextValue, type IslandPhase, type IslandProps, IslandTrigger, type IslandTriggerProps, Item, type ItemProps, List, type ListProps, Logo, type LogoProps, NewComment, type NewCommentProps, OtherDrafts, type OtherDraftsProps, PickButton, type PickButtonProps, type PointerEventLike, SETTINGS_COPY, STAGGER_ROWS, Settings, type SettingsProps, TIME_DELAY_MS, UNSENT_COPY, Unsent, type UnsentProps, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, type WordmarkProps, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
|
package/dist/island/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { Item, TIME_DELAY_MS } from "./item.js";
|
|
|
16
16
|
import { List } from "./list.js";
|
|
17
17
|
import { NewComment, PickButton } from "./new-comment.js";
|
|
18
18
|
import { Settings } from "./settings.js";
|
|
19
|
+
import { ImportDrafts, OtherDrafts } from "./transfer.js";
|
|
19
20
|
import { IslandTrigger } from "./trigger.js";
|
|
20
21
|
import { Unsent } from "./unsent.js";
|
|
21
|
-
export { APPROVE_COPY, Account, Approve, Branch, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, FILTER_LABELS, Filters, Header, ISLAND_COPY, Island, IslandContent, IslandContextError, IslandTrigger, Item, List, Logo, NewComment, PickButton, SETTINGS_COPY, STAGGER_ROWS, Settings, TIME_DELAY_MS, UNSENT_COPY, Unsent, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
|
|
22
|
+
export { APPROVE_COPY, Account, Approve, Branch, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, FILTER_LABELS, Filters, Header, ISLAND_COPY, ImportDrafts, Island, IslandContent, IslandContextError, IslandTrigger, Item, List, Logo, NewComment, OtherDrafts, PickButton, SETTINGS_COPY, STAGGER_ROWS, Settings, TIME_DELAY_MS, UNSENT_COPY, Unsent, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
|
|
@@ -53,10 +53,13 @@ export declare const UNSENT_COPY: {
|
|
|
53
53
|
readonly copy: "Copy";
|
|
54
54
|
readonly copied: "Copied";
|
|
55
55
|
readonly copyHint: "Every unsent comment as markdown, to paste into an agent or a ticket.";
|
|
56
|
+
readonly export: "Export";
|
|
57
|
+
readonly exportHint: "Every unsent comment as a file another browser can import.";
|
|
56
58
|
readonly resumeHint: "Open it again";
|
|
57
59
|
readonly discard: "Throw this one away";
|
|
58
60
|
readonly discardGlyph: "✕";
|
|
59
61
|
readonly blank: "(nothing written yet)";
|
|
62
|
+
readonly elsewhere: (path: string) => string;
|
|
60
63
|
};
|
|
61
64
|
/** Copy with no better home than a name. */
|
|
62
65
|
export declare const ISLAND_COPY: {
|
package/dist/island/language.js
CHANGED
|
@@ -104,10 +104,35 @@ const UNSENT_COPY = {
|
|
|
104
104
|
copy: "Copy",
|
|
105
105
|
copied: "Copied",
|
|
106
106
|
copyHint: "Every unsent comment as markdown, to paste into an agent or a ticket.",
|
|
107
|
+
export: "Export",
|
|
108
|
+
exportHint: "Every unsent comment as a file another browser can import.",
|
|
107
109
|
resumeHint: "Open it again",
|
|
108
110
|
discard: "Throw this one away",
|
|
109
111
|
discardGlyph: "✕",
|
|
110
|
-
blank: "(nothing written yet)"
|
|
112
|
+
blank: "(nothing written yet)",
|
|
113
|
+
elsewhere: (path) => `On another page · ${path}`
|
|
114
|
+
};
|
|
115
|
+
const TRANSFER_COPY = {
|
|
116
|
+
open: "Import drafts",
|
|
117
|
+
close: "Close import",
|
|
118
|
+
boxLabel: "An exported drafts file",
|
|
119
|
+
boxHint: "Paste an export here, or drop its file",
|
|
120
|
+
fileLabel: "Choose an exported drafts file",
|
|
121
|
+
add: "Add",
|
|
122
|
+
addHere: "Add here",
|
|
123
|
+
cancel: "Cancel",
|
|
124
|
+
unreadable: "That is not a Maple drafts export.",
|
|
125
|
+
unsupported: "That export is from a newer Maple than this page.",
|
|
126
|
+
ask: (branch, count) => `${String(count)} ${count === 1 ? "draft was" : "drafts were"} written on ${branch}. Add ${count === 1 ? "it" : "them"} here?`,
|
|
127
|
+
result: (result) => [
|
|
128
|
+
`Added ${String(result.added)}`,
|
|
129
|
+
result.skipped > 0 ? `${String(result.skipped)} already here` : "",
|
|
130
|
+
result.expired > 0 ? `${String(result.expired)} too old` : "",
|
|
131
|
+
result.invalid > 0 ? `${String(result.invalid)} not drafts` : ""
|
|
132
|
+
].filter(Boolean).join(" · "),
|
|
133
|
+
found: (count) => `${String(count)} ${count === 1 ? "draft" : "drafts"} saved under`,
|
|
134
|
+
move: "Move here",
|
|
135
|
+
dismiss: "Dismiss"
|
|
111
136
|
};
|
|
112
137
|
const ISLAND_COPY = {
|
|
113
138
|
title: "Comments",
|
|
@@ -140,4 +165,4 @@ function kindPhrase(kind, human) {
|
|
|
140
165
|
return kind === "region" ? `an area of ${human}` : human;
|
|
141
166
|
}
|
|
142
167
|
//#endregion
|
|
143
|
-
export { ACCOUNT_COPY, APPROVE_COPY, CORNER_LABELS, CORNER_ORDER, FILTERS_LABEL, FILTER_LABELS, FILTER_ORDER, ISLAND_COPY, PICK_LABELS, SETTINGS_COPY, TALLY_ORDER, THEME_LABELS, THEME_TITLES, UNSENT_COPY, kindPhrase, openLabel, pickTitle, tallyTitle, triggerLabel };
|
|
168
|
+
export { ACCOUNT_COPY, APPROVE_COPY, CORNER_LABELS, CORNER_ORDER, FILTERS_LABEL, FILTER_LABELS, FILTER_ORDER, ISLAND_COPY, PICK_LABELS, SETTINGS_COPY, TALLY_ORDER, THEME_LABELS, THEME_TITLES, TRANSFER_COPY, UNSENT_COPY, kindPhrase, openLabel, pickTitle, tallyTitle, triggerLabel };
|
package/dist/island/list.js
CHANGED
|
@@ -3,6 +3,7 @@ import { renderPart } from "../part.js";
|
|
|
3
3
|
import { FILTER_LABELS, ISLAND_COPY } from "./language.js";
|
|
4
4
|
import { byReason } from "./comments.js";
|
|
5
5
|
import { listId, reasonOf, useIsland } from "./context.js";
|
|
6
|
+
import { isSetupGap } from "../notice/language.js";
|
|
6
7
|
import { Fragment, createElement, forwardRef } from "react";
|
|
7
8
|
import { useComments, useMaple } from "@maple-kit/react";
|
|
8
9
|
//#region src/island/list.ts
|
|
@@ -10,9 +11,9 @@ const PART = "<Maple.List>";
|
|
|
10
11
|
const List = forwardRef(function List(props, ref) {
|
|
11
12
|
const { asChild, children, className, ...rest } = props;
|
|
12
13
|
const island = useIsland(PART);
|
|
13
|
-
const { error, filter, phase } = useMaple();
|
|
14
|
+
const { error, filter, github, phase } = useMaple();
|
|
14
15
|
const comments = useComments();
|
|
15
|
-
const failed = error?.during === "load";
|
|
16
|
+
const failed = error?.during === "load" && !isSetupGap(error.kind, github);
|
|
16
17
|
const rows = filter === "unpinned" ? byReason(comments, (comment) => reasonOf(island.resolutions, comment.id)) : comments;
|
|
17
18
|
const body = rows.length === 0 ? renderPart("p", false, { className: "mk-empty" }, nothing(phase, failed)) : rows.map((comment) => createElement(Fragment, { key: comment.id }, children?.(comment)));
|
|
18
19
|
return renderPart("div", asChild, {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PartProps } from "../part.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/island/transfer.d.ts
|
|
4
|
+
/** The section. Its children replace everything inside it. */
|
|
5
|
+
export interface ImportDraftsProps extends PartProps {
|
|
6
|
+
readonly children?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/** The row. Its children replace everything inside it. */
|
|
9
|
+
export interface OtherDraftsProps extends PartProps {
|
|
10
|
+
readonly children?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/** Import from a paste or a file, drawn as one quiet button until it is used. */
|
|
13
|
+
export declare const ImportDrafts: import("react").ForwardRefExoticComponent<ImportDraftsProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
14
|
+
/** A row that says drafts exist under another branch here, and offers to bring them. */
|
|
15
|
+
export declare const OtherDrafts: import("react").ForwardRefExoticComponent<OtherDraftsProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
16
|
+
//#endregion
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { cx } from "../cx.js";
|
|
2
|
+
import { renderPart } from "../part.js";
|
|
3
|
+
import { TRANSFER_COPY } from "./language.js";
|
|
4
|
+
import { createElement, forwardRef, useState } from "react";
|
|
5
|
+
import { useMapleClient } from "@maple-kit/react";
|
|
6
|
+
//#region src/island/transfer.ts
|
|
7
|
+
function said(result) {
|
|
8
|
+
return TRANSFER_COPY.result(result);
|
|
9
|
+
}
|
|
10
|
+
const ImportDrafts = forwardRef(function ImportDrafts(props, ref) {
|
|
11
|
+
const { asChild, children, className, ...rest } = props;
|
|
12
|
+
const client = useMapleClient();
|
|
13
|
+
const [open, setOpen] = useState(false);
|
|
14
|
+
const [text, setText] = useState("");
|
|
15
|
+
const [note, setNote] = useState();
|
|
16
|
+
const [asking, setAsking] = useState();
|
|
17
|
+
const settle = (outcome) => {
|
|
18
|
+
setNote(outcome.ok ? said(outcome) : TRANSFER_COPY[outcome.reason]);
|
|
19
|
+
if (outcome.ok) setText("");
|
|
20
|
+
setAsking(void 0);
|
|
21
|
+
};
|
|
22
|
+
const add = () => {
|
|
23
|
+
const preview = client.previewDraftImport(text);
|
|
24
|
+
if (!preview.ok) return settle(preview);
|
|
25
|
+
if (preview.sameBranch) return settle(client.importDrafts(text));
|
|
26
|
+
setNote(void 0);
|
|
27
|
+
setAsking(preview);
|
|
28
|
+
};
|
|
29
|
+
const load = (file) => {
|
|
30
|
+
file?.text().then(setText, () => setNote(TRANSFER_COPY.unreadable));
|
|
31
|
+
};
|
|
32
|
+
const body = asking ? createElement("div", {
|
|
33
|
+
key: "ask",
|
|
34
|
+
className: "mk-transfer-ask",
|
|
35
|
+
role: "alert"
|
|
36
|
+
}, createElement("span", null, TRANSFER_COPY.ask(asking.branch, asking.count)), button(TRANSFER_COPY.addHere, () => settle(client.importDrafts(text))), button(TRANSFER_COPY.cancel, () => setAsking(void 0))) : [createElement("textarea", {
|
|
37
|
+
key: "box",
|
|
38
|
+
className: "mk-transfer-box",
|
|
39
|
+
"aria-label": TRANSFER_COPY.boxLabel,
|
|
40
|
+
placeholder: TRANSFER_COPY.boxHint,
|
|
41
|
+
value: text,
|
|
42
|
+
rows: 3,
|
|
43
|
+
onChange: (event) => setText(event.target.value),
|
|
44
|
+
onDragOver: (event) => event.preventDefault(),
|
|
45
|
+
onDrop: (event) => {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
load(event.dataTransfer.files[0]);
|
|
48
|
+
}
|
|
49
|
+
}), createElement("div", {
|
|
50
|
+
key: "row",
|
|
51
|
+
className: "mk-transfer-row"
|
|
52
|
+
}, createElement("input", {
|
|
53
|
+
type: "file",
|
|
54
|
+
accept: "application/json,.json",
|
|
55
|
+
className: "mk-transfer-file",
|
|
56
|
+
"aria-label": TRANSFER_COPY.fileLabel,
|
|
57
|
+
onChange: (event) => load(event.target.files?.[0])
|
|
58
|
+
}), button(TRANSFER_COPY.add, add, text.trim() === ""))];
|
|
59
|
+
return renderPart("div", asChild, {
|
|
60
|
+
...rest,
|
|
61
|
+
className: cx("mk-transfer", className),
|
|
62
|
+
ref
|
|
63
|
+
}, children ?? [
|
|
64
|
+
button(open ? TRANSFER_COPY.close : TRANSFER_COPY.open, () => setOpen(!open), false, {
|
|
65
|
+
key: "toggle",
|
|
66
|
+
"aria-expanded": open
|
|
67
|
+
}),
|
|
68
|
+
open ? createElement("div", {
|
|
69
|
+
key: "panel",
|
|
70
|
+
className: "mk-transfer-panel"
|
|
71
|
+
}, body) : null,
|
|
72
|
+
note === void 0 ? null : createElement("p", {
|
|
73
|
+
key: "note",
|
|
74
|
+
className: "mk-transfer-note",
|
|
75
|
+
role: "status"
|
|
76
|
+
}, note)
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
const OtherDrafts = forwardRef(function OtherDrafts(props, ref) {
|
|
80
|
+
const { asChild, children, className, ...rest } = props;
|
|
81
|
+
const client = useMapleClient();
|
|
82
|
+
const [found, setFound] = useState(() => client.foreignDrafts());
|
|
83
|
+
const [first] = found;
|
|
84
|
+
if (first === void 0) return null;
|
|
85
|
+
const refresh = () => setFound(client.foreignDrafts());
|
|
86
|
+
return renderPart("div", asChild, {
|
|
87
|
+
...rest,
|
|
88
|
+
className: cx("mk-transfer-other", className),
|
|
89
|
+
ref
|
|
90
|
+
}, children ?? [
|
|
91
|
+
createElement("span", {
|
|
92
|
+
key: "said",
|
|
93
|
+
className: "mk-transfer-said"
|
|
94
|
+
}, `${TRANSFER_COPY.found(first.count)} `, createElement("code", null, first.branch)),
|
|
95
|
+
button(TRANSFER_COPY.move, () => {
|
|
96
|
+
client.moveDrafts(first.branch);
|
|
97
|
+
refresh();
|
|
98
|
+
}),
|
|
99
|
+
button(TRANSFER_COPY.dismiss, () => {
|
|
100
|
+
client.dismissDrafts(first.branch);
|
|
101
|
+
refresh();
|
|
102
|
+
})
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
function button(label, onClick, disabled = false, more = {}) {
|
|
106
|
+
return createElement("button", {
|
|
107
|
+
type: "button",
|
|
108
|
+
className: "mk-unsent-copy",
|
|
109
|
+
key: label,
|
|
110
|
+
onClick,
|
|
111
|
+
disabled,
|
|
112
|
+
...more
|
|
113
|
+
}, label);
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
export { ImportDrafts, OtherDrafts };
|
package/dist/island/unsent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { renderPart } from "../part.js";
|
|
3
3
|
import { UNSENT_COPY } from "./language.js";
|
|
4
|
+
import { draftRoute, isOnPage, pathOf, usePathname } from "../marks/route.js";
|
|
4
5
|
import { createElement, forwardRef, useState } from "react";
|
|
5
6
|
import { useMaple, useMapleClient } from "@maple-kit/react";
|
|
6
7
|
//#region src/island/unsent.ts
|
|
@@ -10,6 +11,7 @@ const Unsent = forwardRef(function Unsent(props, ref) {
|
|
|
10
11
|
const { drafts, publishing } = useMaple();
|
|
11
12
|
const client = useMapleClient();
|
|
12
13
|
const [copied, setCopied] = useState(false);
|
|
14
|
+
const pathname = usePathname(typeof window === "undefined" ? void 0 : window);
|
|
13
15
|
if (drafts.length === 0) return null;
|
|
14
16
|
const copy = () => {
|
|
15
17
|
navigator.clipboard.writeText(client.draftsAsMarkdown()).then(() => {
|
|
@@ -17,6 +19,7 @@ const Unsent = forwardRef(function Unsent(props, ref) {
|
|
|
17
19
|
setTimeout(() => setCopied(false), COPIED_MS);
|
|
18
20
|
}, () => void 0);
|
|
19
21
|
};
|
|
22
|
+
const save = (event) => download(event.currentTarget.ownerDocument, "maple-drafts.json", client.draftsAsJson());
|
|
20
23
|
return renderPart("div", asChild, {
|
|
21
24
|
...rest,
|
|
22
25
|
className: cx("mk-unsent", className),
|
|
@@ -30,6 +33,11 @@ const Unsent = forwardRef(function Unsent(props, ref) {
|
|
|
30
33
|
title: UNSENT_COPY.copyHint,
|
|
31
34
|
onClick: copy
|
|
32
35
|
}, copied ? UNSENT_COPY.copied : UNSENT_COPY.copy), createElement("button", {
|
|
36
|
+
type: "button",
|
|
37
|
+
className: "mk-unsent-copy",
|
|
38
|
+
title: UNSENT_COPY.exportHint,
|
|
39
|
+
onClick: save
|
|
40
|
+
}, UNSENT_COPY.export), createElement("button", {
|
|
33
41
|
type: "button",
|
|
34
42
|
className: "mk-btn mk-btn-primary mk-press mk-unsent-publish",
|
|
35
43
|
disabled: publishing,
|
|
@@ -37,9 +45,18 @@ const Unsent = forwardRef(function Unsent(props, ref) {
|
|
|
37
45
|
}, publishing ? UNSENT_COPY.publishing : UNSENT_COPY.publish(drafts.length))), createElement("ul", {
|
|
38
46
|
key: "rows",
|
|
39
47
|
className: "mk-unsent-rows"
|
|
40
|
-
}, ...drafts.map((draft) => row(draft, client)))]);
|
|
48
|
+
}, ...drafts.map((draft) => row(draft, client, pathname)))]);
|
|
41
49
|
});
|
|
42
|
-
function
|
|
50
|
+
function download(page, name, text) {
|
|
51
|
+
const url = URL.createObjectURL(new Blob([text], { type: "application/json" }));
|
|
52
|
+
const link = page.createElement("a");
|
|
53
|
+
link.href = url;
|
|
54
|
+
link.download = name;
|
|
55
|
+
link.click();
|
|
56
|
+
URL.revokeObjectURL(url);
|
|
57
|
+
}
|
|
58
|
+
function row(draft, client, pathname) {
|
|
59
|
+
const route = draftRoute(draft);
|
|
43
60
|
return createElement("li", {
|
|
44
61
|
key: draft.id,
|
|
45
62
|
className: "mk-unsent-row"
|
|
@@ -48,7 +65,10 @@ function row(draft, client) {
|
|
|
48
65
|
className: "mk-unsent-body",
|
|
49
66
|
title: UNSENT_COPY.resumeHint,
|
|
50
67
|
onClick: () => client.resumeDraft(draft.id)
|
|
51
|
-
}, draft.body.trim() === "" ? UNSENT_COPY.blank : draft.body), createElement("
|
|
68
|
+
}, draft.body.trim() === "" ? UNSENT_COPY.blank : draft.body), route === void 0 || isOnPage(draft, pathname) ? null : createElement("a", {
|
|
69
|
+
className: "mk-unsent-where",
|
|
70
|
+
href: route
|
|
71
|
+
}, UNSENT_COPY.elsewhere(pathOf(route) ?? route)), createElement("button", {
|
|
52
72
|
type: "button",
|
|
53
73
|
className: "mk-unsent-drop",
|
|
54
74
|
"aria-label": UNSENT_COPY.discard,
|
package/dist/maple.js
CHANGED
|
@@ -16,6 +16,7 @@ import { Item } from "./island/item.js";
|
|
|
16
16
|
import { List } from "./island/list.js";
|
|
17
17
|
import { NewComment, PickButton } from "./island/new-comment.js";
|
|
18
18
|
import { Settings } from "./island/settings.js";
|
|
19
|
+
import { ImportDrafts, OtherDrafts } from "./island/transfer.js";
|
|
19
20
|
import { IslandTrigger } from "./island/trigger.js";
|
|
20
21
|
import { Unsent } from "./island/unsent.js";
|
|
21
22
|
import { MapleMarkLayer } from "./marks/layer.js";
|
|
@@ -48,7 +49,7 @@ function inventory(branch, label, defaultOpen) {
|
|
|
48
49
|
}, createElement(IslandTrigger), createElement(IslandContent, null, createElement(Header, null, createElement(Logo), createElement(Branch, {
|
|
49
50
|
branch,
|
|
50
51
|
...label === void 0 ? {} : { label }
|
|
51
|
-
}), createElement(Settings)), createElement(MapleNotice, { during: LOAD_CALLS }), createElement(Filters), createElement(List, { children: (comment) => createElement(Item, { comment }) }), createElement(Unsent), createElement(Approve), createElement(NewComment, null, ...PICK_ORDER.map((kind) => createElement(PickButton, {
|
|
52
|
+
}), createElement(Settings)), createElement(MapleNotice, { during: LOAD_CALLS }), createElement(Filters), createElement(List, { children: (comment) => createElement(Item, { comment }) }), createElement(OtherDrafts), createElement(Unsent), createElement(ImportDrafts), createElement(Approve), createElement(NewComment, null, ...PICK_ORDER.map((kind) => createElement(PickButton, {
|
|
52
53
|
key: kind,
|
|
53
54
|
kind
|
|
54
55
|
})))));
|
package/dist/marks/layer.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { useMapleUi } from "../context.js";
|
|
2
2
|
import { hoverHandlers } from "../hover.js";
|
|
3
3
|
import { culled, markSpot, placeMark } from "./geometry.js";
|
|
4
|
+
import { usePathname } from "./route.js";
|
|
4
5
|
import { useFrameLoop, viewportHeight } from "./frame.js";
|
|
5
6
|
import { ringLabel } from "./label.js";
|
|
6
7
|
import { MapleMark } from "./mark.js";
|
|
7
8
|
import { useNudges } from "./nudge.js";
|
|
8
9
|
import { OFF_ATTRIBUTE, flag, place } from "./paint.js";
|
|
9
|
-
import { addresses, draftPlacements, placements } from "./placement.js";
|
|
10
|
+
import { addresses, draftPlacements, placements, rectangleOf } from "./placement.js";
|
|
10
11
|
import { MapleTargetRing } from "./ring.js";
|
|
11
12
|
import { createElement, forwardRef, useCallback, useEffect, useMemo, useRef } from "react";
|
|
12
13
|
import { useMaple, useMapleClient } from "@maple-kit/react";
|
|
13
|
-
import { kindOf, regionBox, resolveAnchor, sourceFor } from "@maple-kit/core/anchor";
|
|
14
|
+
import { kindOf, membersBox, regionBox, resolveAnchor, sourceFor } from "@maple-kit/core/anchor";
|
|
14
15
|
//#region src/marks/layer.ts
|
|
15
16
|
const PART = "Maple.MarkLayer";
|
|
16
17
|
const MapleMarkLayer = forwardRef(function MapleMarkLayer(props, ref) {
|
|
@@ -34,7 +35,12 @@ const MapleMarkLayer = forwardRef(function MapleMarkLayer(props, ref) {
|
|
|
34
35
|
container
|
|
35
36
|
]);
|
|
36
37
|
const drafts = useWaiting(state);
|
|
37
|
-
const
|
|
38
|
+
const pathname = usePathname(container.ownerDocument.defaultView);
|
|
39
|
+
const drafted = useMemo(() => draftPlacements(drafts, container.ownerDocument, pathname), [
|
|
40
|
+
drafts,
|
|
41
|
+
container,
|
|
42
|
+
pathname
|
|
43
|
+
]);
|
|
38
44
|
const nudges = useNudges();
|
|
39
45
|
const nodes = useRef(/* @__PURE__ */ new Map());
|
|
40
46
|
const paint = useCallback(() => {
|
|
@@ -132,6 +138,7 @@ function pointing(client, id) {
|
|
|
132
138
|
};
|
|
133
139
|
}
|
|
134
140
|
function boxOf(placement) {
|
|
141
|
+
if (placement.members) return membersBox(placement.members);
|
|
135
142
|
const box = placement.range?.getBoundingClientRect() ?? placement.element.getBoundingClientRect();
|
|
136
143
|
return placement.region === void 0 ? box : regionBox(box, placement.region);
|
|
137
144
|
}
|
|
@@ -163,6 +170,7 @@ function markProps(placement, view) {
|
|
|
163
170
|
author: comment.author.name,
|
|
164
171
|
on: ringLabel({
|
|
165
172
|
kind: kindOf(comment.anchor),
|
|
173
|
+
anchor: comment.anchor,
|
|
166
174
|
element: placement.element
|
|
167
175
|
}),
|
|
168
176
|
selected: comment.id === view.selectedId,
|
|
@@ -179,6 +187,7 @@ function draftProps(placement, view) {
|
|
|
179
187
|
confidence: placement.confidence,
|
|
180
188
|
on: ringLabel({
|
|
181
189
|
kind: kindOf(draft.anchor),
|
|
190
|
+
anchor: draft.anchor,
|
|
182
191
|
element: placement.element
|
|
183
192
|
}),
|
|
184
193
|
peeked: draft.id === view.peeked,
|
|
@@ -199,8 +208,10 @@ function marked(input, id, state) {
|
|
|
199
208
|
return {
|
|
200
209
|
target: hit.range ?? hit.element,
|
|
201
210
|
...hit.region === void 0 ? {} : { region: hit.region },
|
|
211
|
+
...hit.members === void 0 ? {} : { members: hit.members },
|
|
202
212
|
label: ringLabel({
|
|
203
213
|
kind: kindOf(hit.anchor),
|
|
214
|
+
anchor: hit.anchor,
|
|
204
215
|
element: hit.element
|
|
205
216
|
}),
|
|
206
217
|
...noteFor({
|
|
@@ -237,7 +248,7 @@ function composing(target, input) {
|
|
|
237
248
|
if (found.status !== "resolved") return {};
|
|
238
249
|
return {
|
|
239
250
|
target: found.range ?? found.element,
|
|
240
|
-
...target.anchor
|
|
251
|
+
...rectangleOf(target.anchor, found),
|
|
241
252
|
label,
|
|
242
253
|
...noteFor({
|
|
243
254
|
anchor: target.anchor,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Draft } from "@maple-kit/core/overlay";
|
|
2
|
-
import { AnchorRegion } from "@maple-kit/core/anchor";
|
|
2
|
+
import { Anchor, AnchorRegion, PlacedMember } from "@maple-kit/core/anchor";
|
|
3
3
|
import { Comment } from "@maple-kit/core";
|
|
4
4
|
//#region src/marks/placement.d.ts
|
|
5
5
|
/** Where on the page something is, whether it was published or not. */
|
|
@@ -9,6 +9,8 @@ export interface Located {
|
|
|
9
9
|
readonly range?: Range;
|
|
10
10
|
/** The rectangle, when the comment is on a region rather than an element. */
|
|
11
11
|
readonly region?: AnchorRegion;
|
|
12
|
+
/** The elements a region covers, when they placed it: the rectangle follows them. */
|
|
13
|
+
readonly members?: readonly PlacedMember[];
|
|
12
14
|
readonly confidence: number;
|
|
13
15
|
}
|
|
14
16
|
/** A comment the page still has somewhere to put. */
|
package/dist/marks/placement.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isOnPage } from "./route.js";
|
|
1
2
|
import { kindOf, resolveAnchor } from "@maple-kit/core/anchor";
|
|
2
3
|
//#region src/marks/placement.ts
|
|
3
4
|
function addresses(comments) {
|
|
@@ -16,8 +17,9 @@ function placements(comments, address, root) {
|
|
|
16
17
|
}
|
|
17
18
|
return placed;
|
|
18
19
|
}
|
|
19
|
-
function draftPlacements(drafts, root) {
|
|
20
|
+
function draftPlacements(drafts, root, pathname) {
|
|
20
21
|
return drafts.flatMap((draft) => {
|
|
22
|
+
if (!isOnPage(draft, pathname)) return [];
|
|
21
23
|
const found = locate(draft.anchor, root);
|
|
22
24
|
return found ? [{
|
|
23
25
|
...found,
|
|
@@ -35,9 +37,13 @@ function locate(anchor, root) {
|
|
|
35
37
|
return {
|
|
36
38
|
element: found.element,
|
|
37
39
|
...found.range === void 0 ? {} : { range: found.range },
|
|
38
|
-
...anchor
|
|
40
|
+
...rectangleOf(anchor, found),
|
|
39
41
|
confidence: found.confidence
|
|
40
42
|
};
|
|
41
43
|
}
|
|
44
|
+
function rectangleOf(anchor, found) {
|
|
45
|
+
if (found.members) return { members: found.members };
|
|
46
|
+
return anchor.region === void 0 ? {} : { region: anchor.region };
|
|
47
|
+
}
|
|
42
48
|
//#endregion
|
|
43
|
-
export { addresses, draftPlacements, placements };
|
|
49
|
+
export { addresses, draftPlacements, placements, rectangleOf };
|
package/dist/marks/ring.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnchorRegion } from "@maple-kit/core/anchor";
|
|
1
|
+
import { AnchorRegion, PlacedMember } from "@maple-kit/core/anchor";
|
|
2
2
|
//#region src/marks/ring.d.ts
|
|
3
3
|
/** Set on the label when it was moved under the anchor to clear page text. */
|
|
4
4
|
export declare const BELOW_ATTRIBUTE = "data-mk-below";
|
|
@@ -18,6 +18,11 @@ export interface TargetRingProps {
|
|
|
18
18
|
* ring is that rectangle rather than the element it was measured in.
|
|
19
19
|
*/
|
|
20
20
|
readonly region?: AnchorRegion;
|
|
21
|
+
/**
|
|
22
|
+
* The elements a region covers. When they are known the ring is the
|
|
23
|
+
* rectangle they grow into, and `region` is not used.
|
|
24
|
+
*/
|
|
25
|
+
readonly members?: readonly PlacedMember[];
|
|
21
26
|
/** The label's words. `ringLabel` builds them; the ring never invents them. */
|
|
22
27
|
readonly label?: string;
|
|
23
28
|
/** A second line under the label. Developer detail: where this is written. */
|
package/dist/marks/ring.js
CHANGED
|
@@ -5,7 +5,7 @@ import { labelSpotFor } from "./clearance.js";
|
|
|
5
5
|
import { useFrameLoop, viewportHeight } from "./frame.js";
|
|
6
6
|
import { MOVING_ATTRIBUTE, OFF_ATTRIBUTE, flag, place } from "./paint.js";
|
|
7
7
|
import { createElement, forwardRef, useCallback, useRef, useState } from "react";
|
|
8
|
-
import { regionBox } from "@maple-kit/core/anchor";
|
|
8
|
+
import { membersBox, regionBox } from "@maple-kit/core/anchor";
|
|
9
9
|
//#region src/marks/ring.ts
|
|
10
10
|
const BELOW_ATTRIBUTE = "data-mk-below";
|
|
11
11
|
const END_ATTRIBUTE = "data-mk-end";
|
|
@@ -16,18 +16,23 @@ function linesOf(target) {
|
|
|
16
16
|
function empty(rect) {
|
|
17
17
|
return rect.width === 0 && rect.height === 0;
|
|
18
18
|
}
|
|
19
|
+
function rectOf(target, region, members) {
|
|
20
|
+
if (members) return membersBox(members);
|
|
21
|
+
const box = target.getBoundingClientRect();
|
|
22
|
+
return region === void 0 ? box : regionBox(box, region);
|
|
23
|
+
}
|
|
19
24
|
const MapleTargetRing = forwardRef(function MapleTargetRing(props, ref) {
|
|
20
|
-
const { className, label, note, region, state = "composing", target } = props;
|
|
25
|
+
const { className, label, members, note, region, state = "composing", target } = props;
|
|
21
26
|
const { container } = useMapleUi(PART);
|
|
22
27
|
const ring = useRef(null);
|
|
23
28
|
const cap = useRef(null);
|
|
24
29
|
const runs = useRef([]);
|
|
25
30
|
const [lines, setLines] = useState(0);
|
|
31
|
+
const drawsRegion = region !== void 0 || members !== void 0;
|
|
26
32
|
const paint = useCallback((moving) => {
|
|
27
33
|
const node = ring.current;
|
|
28
34
|
if (!node || !target) return;
|
|
29
|
-
const
|
|
30
|
-
const rect = region === void 0 ? box : regionBox(box, region);
|
|
35
|
+
const rect = rectOf(target, region, members);
|
|
31
36
|
const away = empty(rect) || culled(rect, viewportHeight(container));
|
|
32
37
|
flag(node, OFF_ATTRIBUTE, away);
|
|
33
38
|
flag(node, MOVING_ATTRIBUTE, moving);
|
|
@@ -35,7 +40,7 @@ const MapleTargetRing = forwardRef(function MapleTargetRing(props, ref) {
|
|
|
35
40
|
const drawn = ringBox(rect);
|
|
36
41
|
place(node, drawn);
|
|
37
42
|
if (cap.current && !moving) corner(cap.current, drawn, container);
|
|
38
|
-
const boxes =
|
|
43
|
+
const boxes = drawsRegion ? [] : linesOf(target);
|
|
39
44
|
if (boxes.length !== lines) setLines(boxes.length);
|
|
40
45
|
boxes.forEach((box, index) => {
|
|
41
46
|
const run = runs.current[index];
|
|
@@ -43,7 +48,9 @@ const MapleTargetRing = forwardRef(function MapleTargetRing(props, ref) {
|
|
|
43
48
|
});
|
|
44
49
|
}, [
|
|
45
50
|
container,
|
|
51
|
+
drawsRegion,
|
|
46
52
|
lines,
|
|
53
|
+
members,
|
|
47
54
|
region,
|
|
48
55
|
target
|
|
49
56
|
]);
|
|
@@ -52,8 +59,8 @@ const MapleTargetRing = forwardRef(function MapleTargetRing(props, ref) {
|
|
|
52
59
|
return createElement("div", {
|
|
53
60
|
className: className ? `mk-ring ${className}` : "mk-ring",
|
|
54
61
|
"data-mk-state": state,
|
|
55
|
-
"data-mk-passage": String(
|
|
56
|
-
"data-mk-region": String(
|
|
62
|
+
"data-mk-passage": String(!drawsRegion && "startContainer" in target),
|
|
63
|
+
"data-mk-region": String(drawsRegion),
|
|
57
64
|
ref: composeRefs(ref, (node) => {
|
|
58
65
|
ring.current = node;
|
|
59
66
|
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
//#region src/marks/route.ts
|
|
3
|
+
const ROUTE_POLL_MS = 400;
|
|
4
|
+
function pathOf(url) {
|
|
5
|
+
if (url === void 0 || url === "") return void 0;
|
|
6
|
+
try {
|
|
7
|
+
const { pathname } = new URL(url, "https://route.invalid");
|
|
8
|
+
return trimmed(pathname);
|
|
9
|
+
} catch {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function trimmed(pathname) {
|
|
14
|
+
let end = pathname.length;
|
|
15
|
+
while (end > 1 && pathname[end - 1] === "/") end -= 1;
|
|
16
|
+
return pathname.slice(0, end);
|
|
17
|
+
}
|
|
18
|
+
function draftRoute(draft) {
|
|
19
|
+
const url = draft.context?.url;
|
|
20
|
+
return pathOf(url) === void 0 ? void 0 : url?.replace(/^\w+:\/\/[^/]*/, "");
|
|
21
|
+
}
|
|
22
|
+
function isOnPage(draft, pathname) {
|
|
23
|
+
const path = pathOf(draft.context?.url);
|
|
24
|
+
return path === void 0 || path === pathOf(pathname);
|
|
25
|
+
}
|
|
26
|
+
function usePathname(view) {
|
|
27
|
+
return useSyncExternalStore((notify) => {
|
|
28
|
+
view?.addEventListener("popstate", notify);
|
|
29
|
+
const timer = setInterval(notify, ROUTE_POLL_MS);
|
|
30
|
+
return () => {
|
|
31
|
+
view?.removeEventListener("popstate", notify);
|
|
32
|
+
clearInterval(timer);
|
|
33
|
+
};
|
|
34
|
+
}, () => view?.location.pathname ?? "/", () => "/");
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { draftRoute, isOnPage, pathOf, usePathname };
|
package/dist/mock/language.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export declare const MOCK_COPY: {
|
|
|
11
11
|
readonly label: "Mock this page";
|
|
12
12
|
readonly field: "Find a call";
|
|
13
13
|
readonly sentence: "Say a state, like “no items yet”";
|
|
14
|
+
readonly failed: "Couldn't read that. Try again, or set calls below.";
|
|
15
|
+
readonly refused: "The server refused that. Set calls below.";
|
|
14
16
|
readonly unnamed: "That doesn't name a state this page's data can be in.";
|
|
15
17
|
readonly real: "Real";
|
|
16
18
|
readonly escape: "Esc";
|
package/dist/mock/language.js
CHANGED
|
@@ -32,6 +32,8 @@ const MOCK_COPY = {
|
|
|
32
32
|
label: "Mock this page",
|
|
33
33
|
field: "Find a call",
|
|
34
34
|
sentence: "Say a state, like “no items yet”",
|
|
35
|
+
failed: "Couldn't read that. Try again, or set calls below.",
|
|
36
|
+
refused: "The server refused that. Set calls below.",
|
|
35
37
|
unnamed: "That doesn't name a state this page's data can be in.",
|
|
36
38
|
real: "Real",
|
|
37
39
|
escape: "Esc",
|
package/dist/mock/layers.js
CHANGED
|
@@ -13,7 +13,8 @@ const LAYER_COPY = {
|
|
|
13
13
|
off: "Off",
|
|
14
14
|
realValue: "Real value",
|
|
15
15
|
notEvaluated: "Named by the mock, not evaluated on this page",
|
|
16
|
-
serverActs: "The server still acts as you."
|
|
16
|
+
serverActs: "The server still acts as you.",
|
|
17
|
+
reloads: "reloads the page"
|
|
17
18
|
};
|
|
18
19
|
function Layers(props) {
|
|
19
20
|
const { client, state } = props;
|
|
@@ -24,6 +25,8 @@ function Layers(props) {
|
|
|
24
25
|
const grants = [];
|
|
25
26
|
const real = state.realAs;
|
|
26
27
|
const roles = identity?.role?.values ?? [];
|
|
28
|
+
const reloading = identity?.server !== void 0;
|
|
29
|
+
const titled = (title) => reloading ? `${title} · ${LAYER_COPY.reloads}` : title;
|
|
27
30
|
if (roles.length > 0) {
|
|
28
31
|
const role = state.draftAs?.role;
|
|
29
32
|
rows.push(row({
|
|
@@ -60,8 +63,8 @@ function Layers(props) {
|
|
|
60
63
|
return createElement("div", {
|
|
61
64
|
className: "mk-mock-layers",
|
|
62
65
|
ref: panel
|
|
63
|
-
}, rows.length === 0 ? null : section(LAYER_COPY.shownAs, rows), grants.length === 0 ? null : createElement(Fold, {
|
|
64
|
-
title: LAYER_COPY.permissions,
|
|
66
|
+
}, rows.length === 0 ? null : section(titled(LAYER_COPY.shownAs), rows), grants.length === 0 ? null : createElement(Fold, {
|
|
67
|
+
title: titled(LAYER_COPY.permissions),
|
|
65
68
|
rows: grants
|
|
66
69
|
}), flags.length === 0 ? null : createElement(Fold, {
|
|
67
70
|
title: LAYER_COPY.flags,
|
package/dist/mock/mock.js
CHANGED
|
@@ -101,7 +101,7 @@ function Box(props) {
|
|
|
101
101
|
value: state.query,
|
|
102
102
|
ref: field,
|
|
103
103
|
onChange: (event) => client.setQuery(event.currentTarget.value)
|
|
104
|
-
}), createElement("kbd", { className: "mk-mock-key-hint" }, MOCK_COPY.escape)), state
|
|
104
|
+
}), createElement("kbd", { className: "mk-mock-key-hint" }, MOCK_COPY.escape)), aside(state) && createElement("p", { className: "mk-mock-unnamed" }, aside(state)), createElement("div", { className: "mk-mock-body" }, createElement("p", { className: "mk-mock-route" }, `${MOCK_COPY.routePrefix} `, createElement("span", { className: "mk-mono" }, state.route)), calls(state, client), layers && createElement(layers.Layers, {
|
|
105
105
|
state,
|
|
106
106
|
client
|
|
107
107
|
})), createElement(Foot, {
|
|
@@ -109,6 +109,10 @@ function Box(props) {
|
|
|
109
109
|
state
|
|
110
110
|
}));
|
|
111
111
|
}
|
|
112
|
+
function aside({ planFailure, unnamed }) {
|
|
113
|
+
if (planFailure !== void 0) return MOCK_COPY[planFailure];
|
|
114
|
+
return unnamed ? MOCK_COPY.unnamed : void 0;
|
|
115
|
+
}
|
|
112
116
|
function calls(state, client) {
|
|
113
117
|
if (state.calls.length === 0) {
|
|
114
118
|
const said = state.query === "" ? MOCK_COPY.nothingRecorded : MOCK_COPY.nothingMatches;
|
package/dist/notice/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { noticeCss } from "./css.js";
|
|
2
|
-
import { NOTICE_COPY, offersSignIn } from "./language.js";
|
|
2
|
+
import { NOTICE_COPY, isSetupGap, offersSignIn } from "./language.js";
|
|
3
3
|
import { MapleNotice, MapleNoticeProps } from "./notice.js";
|
|
4
|
-
export { MapleNotice, type MapleNoticeProps, NOTICE_COPY, MapleNotice as Notice, noticeCss, offersSignIn };
|
|
4
|
+
export { MapleNotice, type MapleNoticeProps, NOTICE_COPY, MapleNotice as Notice, isSetupGap, noticeCss, offersSignIn };
|
package/dist/notice/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { noticeCss } from "./css.js";
|
|
2
|
-
import { NOTICE_COPY, offersSignIn } from "./language.js";
|
|
2
|
+
import { NOTICE_COPY, isSetupGap, offersSignIn } from "./language.js";
|
|
3
3
|
import { MapleNotice } from "./notice.js";
|
|
4
|
-
export { MapleNotice, NOTICE_COPY, MapleNotice as Notice, noticeCss, offersSignIn };
|
|
4
|
+
export { MapleNotice, NOTICE_COPY, MapleNotice as Notice, isSetupGap, noticeCss, offersSignIn };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FailureKind } from "@maple-kit/core/client";
|
|
1
|
+
import { FailureKind, GitHubLink } from "@maple-kit/core/client";
|
|
2
2
|
//#region src/notice/language.d.ts
|
|
3
3
|
/** The notice's own words. */
|
|
4
4
|
export declare const NOTICE_COPY: {
|
|
@@ -12,4 +12,7 @@ export declare const NOTICE_COPY: {
|
|
|
12
12
|
};
|
|
13
13
|
/** Whether the offer is a sign-in or a retry. A 401 is nobody's to retry. */
|
|
14
14
|
export declare function offersSignIn(kind: FailureKind, linkable: boolean): boolean;
|
|
15
|
+
/** A 401 with no sign-in on offer is a setup gap, not the reviewer's doing:
|
|
16
|
+
* "sign in" would point at a door that is not there. */
|
|
17
|
+
export declare function isSetupGap(kind: FailureKind, github: GitHubLink): boolean;
|
|
15
18
|
//#endregion
|
package/dist/notice/language.js
CHANGED
|
@@ -10,5 +10,8 @@ const NOTICE_COPY = {
|
|
|
10
10
|
function offersSignIn(kind, linkable) {
|
|
11
11
|
return kind === "unauthorized" && linkable;
|
|
12
12
|
}
|
|
13
|
+
function isSetupGap(kind, github) {
|
|
14
|
+
return kind === "unauthorized" && github.state === "unsupported";
|
|
15
|
+
}
|
|
13
16
|
//#endregion
|
|
14
|
-
export { NOTICE_COPY, offersSignIn };
|
|
17
|
+
export { NOTICE_COPY, isSetupGap, offersSignIn };
|
package/dist/notice/notice.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { renderPart } from "../part.js";
|
|
3
|
-
import { NOTICE_COPY, offersSignIn } from "./language.js";
|
|
3
|
+
import { NOTICE_COPY, isSetupGap, offersSignIn } from "./language.js";
|
|
4
4
|
import { createElement, forwardRef } from "react";
|
|
5
5
|
import { useMaple, useMapleClient } from "@maple-kit/react";
|
|
6
6
|
//#region src/notice/notice.ts
|
|
@@ -31,7 +31,7 @@ const MapleNotice = forwardRef(function MapleNotice(props, ref) {
|
|
|
31
31
|
]);
|
|
32
32
|
});
|
|
33
33
|
function said(error, github) {
|
|
34
|
-
return error.kind
|
|
34
|
+
return isSetupGap(error.kind, github) ? NOTICE_COPY.noSignIn : error.message;
|
|
35
35
|
}
|
|
36
36
|
function offer(error, github, client) {
|
|
37
37
|
if (offersSignIn(error.kind, github.state === "failed" || github.state === "unlinked")) return button(NOTICE_COPY.signIn, () => void client.linkGitHub());
|
package/dist/picker/target.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toCommentContext } from "@maple-kit/core/overlay";
|
|
2
|
-
import { describeElement, describeRange, labelFor, regionOf } from "@maple-kit/core/anchor";
|
|
2
|
+
import { captureMembers, describeElement, describeRange, labelFor, regionOf } from "@maple-kit/core/anchor";
|
|
3
3
|
//#region src/picker/target.ts
|
|
4
4
|
function elementOf(pick) {
|
|
5
5
|
return pick.kind === "text" ? rangeElement(pick.range) : pick.element;
|
|
@@ -24,9 +24,11 @@ function anchorFor(pick, element, root) {
|
|
|
24
24
|
if (pick.kind === "text") return describeRange(pick.range, options);
|
|
25
25
|
const described = describeElement(element, options);
|
|
26
26
|
if (pick.kind !== "region") return described;
|
|
27
|
+
const members = captureMembers(pick.rect, options);
|
|
27
28
|
return {
|
|
28
29
|
...described,
|
|
29
|
-
region: regionOf(pick.rect, element.getBoundingClientRect())
|
|
30
|
+
region: regionOf(pick.rect, element.getBoundingClientRect()),
|
|
31
|
+
...members.length === 0 ? {} : { members }
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
function rangeElement(range) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maple-kit/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Maple's composed reviewer parts: the marks, the island and the composer",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"./package.json": "./package.json"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@maple-kit/
|
|
37
|
-
"@maple-kit/
|
|
38
|
-
"@maple-kit/
|
|
36
|
+
"@maple-kit/core": "0.15.0",
|
|
37
|
+
"@maple-kit/mock": "0.15.0",
|
|
38
|
+
"@maple-kit/react": "0.15.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^18.2.0 || ^19.0.0",
|