Skip to content

Commit 8840fe0

Browse files
committed
Merge branch 'main' into lunny/fix_get_fork
2 parents 05e8968 + 9875f9b commit 8840fe0

File tree

6 files changed

+63
-12
lines changed

6 files changed

+63
-12
lines changed

web_src/css/modules/tippy.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
z-index: 1;
2929
}
3030

31+
.tippy-box[data-theme="default"] {
32+
box-shadow: 0 6px 18px var(--color-shadow);
33+
}
34+
3135
/* bare theme, no styling at all, except box-shadow */
3236
.tippy-box[data-theme="bare"] {
3337
border: none;

web_src/css/repo.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,7 @@ td .commit-summary {
16301630
}
16311631

16321632
.repo-button-row-left {
1633-
flex: 1;
1633+
flex-grow: 1;
16341634
}
16351635

16361636
.repo-button-row .button {

web_src/css/repo/clone.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
.clone-panel-tab .item {
2121
padding: 5px 10px;
2222
background: none;
23+
color: var(--color-text-light-2);
2324
}
2425

2526
.clone-panel-tab .item.active {
26-
border-bottom: 3px solid var(--color-secondary);
27+
color: var(--color-text-dark);
28+
border-bottom: 3px solid currentcolor;
2729
}
2830

2931
.clone-panel-tab + .divider {

web_src/js/features/comp/TextExpander.ts

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,31 @@ import {getIssueColor, getIssueIcon} from '../issue.ts';
77
import {debounce} from 'perfect-debounce';
88
import type TextExpanderElement from '@github/text-expander-element';
99

10-
const debouncedSuggestIssues = debounce((key: string, text: string) => new Promise<{matched:boolean; fragment?: HTMLElement}>(async (resolve) => {
10+
type TextExpanderProvideResult = {
11+
matched: boolean,
12+
fragment?: HTMLElement,
13+
}
14+
15+
type TextExpanderChangeEvent = Event & {
16+
detail?: {
17+
key: string,
18+
text: string,
19+
provide: (result: TextExpanderProvideResult | Promise<TextExpanderProvideResult>) => void,
20+
}
21+
}
22+
23+
async function fetchIssueSuggestions(key: string, text: string): Promise<TextExpanderProvideResult> {
1124
const issuePathInfo = parseIssueHref(window.location.href);
1225
if (!issuePathInfo.ownerName) {
1326
const repoOwnerPathInfo = parseRepoOwnerPathInfo(window.location.pathname);
1427
issuePathInfo.ownerName = repoOwnerPathInfo.ownerName;
1528
issuePathInfo.repoName = repoOwnerPathInfo.repoName;
1629
// then no issuePathInfo.indexString here, it is only used to exclude the current issue when "matchIssue"
1730
}
18-
if (!issuePathInfo.ownerName) return resolve({matched: false});
31+
if (!issuePathInfo.ownerName) return {matched: false};
1932

2033
const matches = await matchIssue(issuePathInfo.ownerName, issuePathInfo.repoName, issuePathInfo.indexString, text);
21-
if (!matches.length) return resolve({matched: false});
34+
if (!matches.length) return {matched: false};
2235

2336
const ul = createElementFromAttrs('ul', {class: 'suggestions'});
2437
for (const issue of matches) {
@@ -30,11 +43,40 @@ const debouncedSuggestIssues = debounce((key: string, text: string) => new Promi
3043
);
3144
ul.append(li);
3245
}
33-
resolve({matched: true, fragment: ul});
34-
}), 100);
46+
return {matched: true, fragment: ul};
47+
}
3548

3649
export function initTextExpander(expander: TextExpanderElement) {
37-
expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}: Record<string, any>) => {
50+
if (!expander) return;
51+
52+
const textarea = expander.querySelector<HTMLTextAreaElement>('textarea');
53+
54+
// help to fix the text-expander "multiword+promise" bug: do not show the popup when there is no "#" before current line
55+
const shouldShowIssueSuggestions = () => {
56+
const posVal = textarea.value.substring(0, textarea.selectionStart);
57+
const lineStart = posVal.lastIndexOf('\n');
58+
const keyStart = posVal.lastIndexOf('#');
59+
return keyStart > lineStart;
60+
};
61+
62+
const debouncedIssueSuggestions = debounce(async (key: string, text: string): Promise<TextExpanderProvideResult> => {
63+
// https://github.com/github/text-expander-element/issues/71
64+
// Upstream bug: when using "multiword+promise", TextExpander will get wrong "key" position.
65+
// To reproduce, comment out the "shouldShowIssueSuggestions" check, use the "await sleep" below,
66+
// then use content "close #20\nclose #20\nclose #20" (3 lines), keep changing the last line `#20` part from the end (including removing the `#`)
67+
// There will be a JS error: Uncaught (in promise) IndexSizeError: Failed to execute 'setStart' on 'Range': The offset 28 is larger than the node's length (27).
68+
69+
// check the input before the request, to avoid emitting empty query to backend (still related to the upstream bug)
70+
if (!shouldShowIssueSuggestions()) return {matched: false};
71+
// await sleep(Math.random() * 1000); // help to reproduce the text-expander bug
72+
const ret = await fetchIssueSuggestions(key, text);
73+
// check the input again to avoid text-expander using incorrect position (upstream bug)
74+
if (!shouldShowIssueSuggestions()) return {matched: false};
75+
return ret;
76+
}, 300); // to match onInputDebounce delay
77+
78+
expander.addEventListener('text-expander-change', (e: TextExpanderChangeEvent) => {
79+
const {key, text, provide} = e.detail;
3880
if (key === ':') {
3981
const matches = matchEmoji(text);
4082
if (!matches.length) return provide({matched: false});
@@ -82,10 +124,11 @@ export function initTextExpander(expander: TextExpanderElement) {
82124

83125
provide({matched: true, fragment: ul});
84126
} else if (key === '#') {
85-
provide(debouncedSuggestIssues(key, text));
127+
provide(debouncedIssueSuggestions(key, text));
86128
}
87129
});
88-
expander?.addEventListener('text-expander-value', ({detail}: Record<string, any>) => {
130+
131+
expander.addEventListener('text-expander-value', ({detail}: Record<string, any>) => {
89132
if (detail?.item) {
90133
// add a space after @mentions and #issue as it's likely the user wants one
91134
const suffix = ['@', '#'].includes(detail.key) ? ' ' : '';

web_src/js/features/repo-common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ function initClonePanelButton(btn: HTMLButtonElement) {
9999
placement: 'bottom-end',
100100
interactive: true,
101101
hideOnClick: true,
102+
arrow: false,
102103
});
103104
}
104105

web_src/js/modules/tippy.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,17 @@ export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
4242
visibleInstances.add(instance);
4343
return onShow?.(instance);
4444
},
45-
arrow: arrow || (theme === 'bare' ? false : arrowSvg),
45+
arrow: arrow ?? (theme === 'bare' ? false : arrowSvg),
4646
// HTML role attribute, ideally the default role would be "popover" but it does not exist
4747
role: role || 'menu',
4848
// CSS theme, either "default", "tooltip", "menu", "box-with-header" or "bare"
4949
theme: theme || role || 'default',
50+
offset: [0, arrow ? 10 : 6],
5051
plugins: [followCursor],
5152
...other,
5253
} satisfies Partial<Props>);
5354

54-
if (role === 'menu') {
55+
if (instance.props.role === 'menu') {
5556
target.setAttribute('aria-haspopup', 'true');
5657
}
5758

0 commit comments

Comments
 (0)