@arcblock/terminal 3.1.21 → 3.1.23
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/lib/Player.js +137 -119
- package/lib/Terminal.js +30 -30
- package/lib/styles.js +20 -11
- package/package.json +4 -4
- package/src/Player.jsx +34 -3
- package/src/Player.stories.jsx +1 -0
- package/src/Terminal.jsx +12 -3
- package/src/styles.js +21 -11
package/lib/Player.js
CHANGED
|
@@ -1,114 +1,132 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useRef as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { PlayerRoot as
|
|
8
|
-
import { defaultOptions as
|
|
9
|
-
const
|
|
10
|
-
function
|
|
11
|
-
const s = Object.assign({},
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
cols:
|
|
15
|
-
rows:
|
|
16
|
-
cursorStyle:
|
|
17
|
-
cursorBlink:
|
|
18
|
-
fontFamily:
|
|
19
|
-
fontSize:
|
|
20
|
-
lineHeight:
|
|
21
|
-
letterSpacing:
|
|
1
|
+
import { jsxs as S, jsx as a } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as g, useReducer as H, useCallback as f, useEffect as q } from "react";
|
|
3
|
+
import l from "prop-types";
|
|
4
|
+
import F from "lodash/isUndefined";
|
|
5
|
+
import P from "lodash/noop";
|
|
6
|
+
import Y from "./Terminal.js";
|
|
7
|
+
import { PlayerRoot as _ } from "./styles.js";
|
|
8
|
+
import { defaultOptions as U, formatFrames as X, defaultState as $, findFrameAt as D, getPlayerClass as G, getFrameClass as K, formatTime as Q } from "./util.js";
|
|
9
|
+
const se = 8;
|
|
10
|
+
function V({ ...z }) {
|
|
11
|
+
const s = Object.assign({}, z);
|
|
12
|
+
F(s.onComplete) && (s.onComplete = P), F(s.onStart) && (s.onStart = P), F(s.onStop) && (s.onStop = P), F(s.onPause) && (s.onPause = P), F(s.onTick) && (s.onTick = P), F(s.onJump) && (s.onJump = P);
|
|
13
|
+
const r = Object.assign({}, U, s.options), { frames: c, totalDuration: h } = X(s.frames, r), J = {
|
|
14
|
+
cols: r.cols,
|
|
15
|
+
rows: r.rows,
|
|
16
|
+
cursorStyle: r.cursorStyle,
|
|
17
|
+
cursorBlink: r.cursorBlink ?? !0,
|
|
18
|
+
fontFamily: r.fontFamily,
|
|
19
|
+
fontSize: r.fontSize,
|
|
20
|
+
lineHeight: r.lineHeight,
|
|
21
|
+
letterSpacing: r.letterSpacing,
|
|
22
22
|
allowTransparency: !0,
|
|
23
23
|
scrollback: 0,
|
|
24
|
-
theme:
|
|
25
|
-
},
|
|
24
|
+
theme: r.enableTheme ? r.theme : {}
|
|
25
|
+
}, I = (e, n) => {
|
|
26
26
|
switch (n.type) {
|
|
27
27
|
case "jump":
|
|
28
|
-
return { ...
|
|
28
|
+
return { ...e, ...n.payload };
|
|
29
29
|
case "start":
|
|
30
|
-
return { ...
|
|
30
|
+
return { ...e, isStarted: !0, lastTickTime: Date.now() };
|
|
31
31
|
case "play":
|
|
32
|
-
return { ...
|
|
32
|
+
return { ...e, isPlaying: !0, lastTickTime: Date.now(), ...n.payload };
|
|
33
33
|
case "pause":
|
|
34
|
-
return { ...
|
|
34
|
+
return { ...e, isPlaying: !1, ...n.payload };
|
|
35
35
|
case "tickStart":
|
|
36
|
-
return { ...
|
|
36
|
+
return { ...e, isRendering: !0, lastTickTime: Date.now(), ...n.payload };
|
|
37
37
|
case "tickEnd":
|
|
38
|
-
return { ...
|
|
38
|
+
return { ...e, isRendering: !1, lastTickTime: Date.now(), ...n.payload };
|
|
39
39
|
case "reset":
|
|
40
|
-
return { ...
|
|
40
|
+
return { ...e, currentFrame: -1, currentTime: 0, ...n.payload };
|
|
41
41
|
default:
|
|
42
|
-
return { ...
|
|
42
|
+
return { ...e, lastTickTime: Date.now(), ...n.payload };
|
|
43
43
|
}
|
|
44
|
-
}, i =
|
|
45
|
-
(
|
|
46
|
-
const
|
|
47
|
-
|
|
44
|
+
}, i = g(null), N = g(null), v = g(null), d = g(null), p = g(null), A = g(!1), [t, u] = H(I, $), j = f(
|
|
45
|
+
(e) => new Promise((n) => {
|
|
46
|
+
const o = c[e];
|
|
47
|
+
o && o.content && i.current ? (t.requireReset && i.current.reset(), i.current.write(o.content, () => {
|
|
48
48
|
n();
|
|
49
49
|
})) : n();
|
|
50
50
|
}),
|
|
51
51
|
[c, t.requireReset]
|
|
52
|
-
),
|
|
53
|
-
(
|
|
54
|
-
const n = c[
|
|
52
|
+
), E = f(
|
|
53
|
+
(e) => {
|
|
54
|
+
const n = c[e];
|
|
55
55
|
n && n.content && i.current && (t.requireReset && i.current.reset(), i.current.write(n.content));
|
|
56
56
|
},
|
|
57
57
|
[c, t.requireReset]
|
|
58
58
|
), m = f(
|
|
59
|
-
(
|
|
60
|
-
typeof s[
|
|
59
|
+
(e) => {
|
|
60
|
+
typeof s[e] == "function" && s[e]({ state: t, frames: c, options: r });
|
|
61
61
|
},
|
|
62
|
-
[s, t, c,
|
|
63
|
-
),
|
|
64
|
-
(
|
|
62
|
+
[s, t, c, r]
|
|
63
|
+
), R = f(
|
|
64
|
+
(e) => {
|
|
65
65
|
if (!i.current) return;
|
|
66
66
|
i.current.reset();
|
|
67
|
-
const n =
|
|
67
|
+
const n = D(c, e);
|
|
68
68
|
if (n >= 0)
|
|
69
|
-
for (let
|
|
70
|
-
|
|
69
|
+
for (let o = 0; o <= n; o++)
|
|
70
|
+
E(o);
|
|
71
71
|
},
|
|
72
|
-
[c,
|
|
73
|
-
),
|
|
74
|
-
(
|
|
75
|
-
if (!
|
|
72
|
+
[c, E]
|
|
73
|
+
), M = f(
|
|
74
|
+
(e) => {
|
|
75
|
+
if (!N.current || !i.current || !t.isStarted)
|
|
76
76
|
return !1;
|
|
77
|
-
const n =
|
|
77
|
+
const n = N.current.getBoundingClientRect().width, o = e.nativeEvent.offsetX, T = Math.floor(h * o / n), b = D(c, T), w = t.isPlaying;
|
|
78
78
|
return u({
|
|
79
79
|
type: "jump",
|
|
80
80
|
payload: {
|
|
81
|
-
currentTime:
|
|
82
|
-
currentFrame:
|
|
81
|
+
currentTime: T,
|
|
82
|
+
currentFrame: b,
|
|
83
83
|
isPlaying: w
|
|
84
84
|
// Keep the same playing state
|
|
85
85
|
}
|
|
86
|
-
}), p.current = null,
|
|
86
|
+
}), p.current = null, R(T), m("onJump"), !1;
|
|
87
87
|
},
|
|
88
|
-
[t.isStarted, t.isPlaying,
|
|
89
|
-
),
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
[t.isStarted, t.isPlaying, h, c, R, m]
|
|
89
|
+
), O = f(() => (t.currentFrame >= c.length - 1 ? (i.current.resize(), u({ type: "reset", payload: { currentFrame: -1, currentTime: 0 } }), i.current && i.current.reset(), p.current = null, u({ type: "play", payload: { currentFrame: -1, currentTime: 0 } })) : (p.current = null, u({ type: "play" })), m("onPlay"), !1), [t.currentFrame, c.length, m]), k = f(() => (t.isStarted === !1 && (i.current.resize(), u({ type: "start" }), i.current && i.current.reset()), p.current = null, u({ type: "play", payload: { currentFrame: -1, currentTime: 0 } }), m("onStart"), !1), [t.isStarted, m]), L = f(() => (u({ type: "pause" }), m("onPause"), !1), [m]);
|
|
90
|
+
q(() => {
|
|
91
|
+
if (!v.current || !r.autoplay || A.current)
|
|
92
|
+
return;
|
|
93
|
+
const e = new IntersectionObserver(
|
|
94
|
+
(n) => {
|
|
95
|
+
const [o] = n;
|
|
96
|
+
o.isIntersecting && !t.isStarted && (k(), A.current = !0, e.unobserve(v.current), e.disconnect());
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
threshold: 0.1,
|
|
100
|
+
// Trigger when 10% of the element is visible
|
|
101
|
+
rootMargin: "0px 0px 0px 0px"
|
|
102
|
+
// Add some margin to avoid triggering too early
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
return e.observe(v.current), () => {
|
|
106
|
+
e.disconnect();
|
|
107
|
+
};
|
|
108
|
+
}, [r.autoplay, t.isStarted, k]), q(() => {
|
|
109
|
+
i.current && (i.current.focus(), r.autoplay || R(Math.min(Math.abs(r.thumbnailTime), h)));
|
|
92
110
|
}, []);
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
if (!
|
|
111
|
+
const x = g(t);
|
|
112
|
+
x.current = t;
|
|
113
|
+
const B = f(async () => {
|
|
114
|
+
const e = x.current;
|
|
115
|
+
if (!e.isPlaying || e.isRendering)
|
|
98
116
|
return;
|
|
99
117
|
const n = performance.now();
|
|
100
118
|
if (!p.current) {
|
|
101
|
-
const y =
|
|
119
|
+
const y = e.currentFrame >= 0 && c[e.currentFrame]?.startTime || 0;
|
|
102
120
|
p.current = n - y;
|
|
103
121
|
}
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
if (m("onComplete"),
|
|
122
|
+
const o = n - p.current, { currentFrame: T } = e, b = T + 1;
|
|
123
|
+
if (T >= c.length - 1) {
|
|
124
|
+
if (m("onComplete"), r.repeat) {
|
|
107
125
|
p.current = n, u({ type: "reset", payload: { currentTime: 0, currentFrame: -1 } });
|
|
108
126
|
return;
|
|
109
127
|
}
|
|
110
128
|
const y = {
|
|
111
|
-
currentTime:
|
|
129
|
+
currentTime: h,
|
|
112
130
|
currentFrame: c.length - 1,
|
|
113
131
|
requireReset: !0,
|
|
114
132
|
isStarted: !1
|
|
@@ -116,92 +134,92 @@ function Q({ ...E }) {
|
|
|
116
134
|
u({ type: "pause", payload: y }), p.current = null;
|
|
117
135
|
return;
|
|
118
136
|
}
|
|
119
|
-
const w = c[
|
|
137
|
+
const w = c[b];
|
|
120
138
|
if (!w) {
|
|
121
|
-
t.isPlaying && (d.current = requestAnimationFrame(
|
|
139
|
+
t.isPlaying && (d.current = requestAnimationFrame(B));
|
|
122
140
|
return;
|
|
123
141
|
}
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
142
|
+
const C = w.startTime || 0;
|
|
143
|
+
if (o >= C) {
|
|
126
144
|
u({
|
|
127
145
|
type: "tickStart",
|
|
128
146
|
payload: {
|
|
129
|
-
currentTime:
|
|
130
|
-
currentFrame:
|
|
147
|
+
currentTime: C,
|
|
148
|
+
currentFrame: b
|
|
131
149
|
}
|
|
132
150
|
});
|
|
133
151
|
try {
|
|
134
|
-
await
|
|
152
|
+
await j(b);
|
|
135
153
|
const y = {
|
|
136
|
-
currentTime:
|
|
137
|
-
currentFrame:
|
|
154
|
+
currentTime: C,
|
|
155
|
+
currentFrame: b
|
|
138
156
|
};
|
|
139
|
-
|
|
157
|
+
e.requireReset && (y.requireReset = !1), u({ type: "tickEnd", payload: y }), m("onTick");
|
|
140
158
|
} catch (y) {
|
|
141
159
|
console.error("Frame rendering error:", y);
|
|
142
160
|
}
|
|
143
161
|
} else
|
|
144
|
-
u({ type: "tick", payload: { currentTime:
|
|
145
|
-
}, [c,
|
|
146
|
-
return
|
|
147
|
-
let
|
|
162
|
+
u({ type: "tick", payload: { currentTime: o } });
|
|
163
|
+
}, [c, j, m, r.repeat, h]);
|
|
164
|
+
return q(() => {
|
|
165
|
+
let e = !0;
|
|
148
166
|
const n = async () => {
|
|
149
|
-
for (;
|
|
150
|
-
await new Promise((
|
|
151
|
-
d.current = requestAnimationFrame(
|
|
152
|
-
}),
|
|
167
|
+
for (; e && x.current.isPlaying; )
|
|
168
|
+
await new Promise((o) => {
|
|
169
|
+
d.current = requestAnimationFrame(o);
|
|
170
|
+
}), e && x.current.isPlaying && await B();
|
|
153
171
|
};
|
|
154
172
|
return t.isPlaying ? n() : (d.current && (cancelAnimationFrame(d.current), d.current = null), p.current = null), () => {
|
|
155
|
-
|
|
173
|
+
e = !1, d.current && (cancelAnimationFrame(d.current), d.current = null);
|
|
156
174
|
};
|
|
157
|
-
}, [t.isPlaying,
|
|
158
|
-
/* @__PURE__ */ a("div", { className: "cover", onClick:
|
|
159
|
-
/* @__PURE__ */ a("div", { className: "start", onClick:
|
|
175
|
+
}, [t.isPlaying, B]), r.controls && (r.frameBox.title = null, r.frameBox.type = null, r.frameBox.style = {}, r.theme?.background === "transparent" ? r.frameBox.style.background = "black" : r.theme?.background && (r.frameBox.style.background = r.theme.background), r.frameBox.style.padding = "10px", r.frameBox.style.paddingBottom = "40px"), /* @__PURE__ */ S(_, { className: G(r, t), ref: v, children: [
|
|
176
|
+
/* @__PURE__ */ a("div", { className: "cover", onClick: k }),
|
|
177
|
+
/* @__PURE__ */ a("div", { className: "start", onClick: k, children: /* @__PURE__ */ S("svg", { style: { enableBackground: "new 0 0 30 30" }, viewBox: "0 0 30 30", children: [
|
|
160
178
|
/* @__PURE__ */ a("polygon", { points: "6.583,3.186 5,4.004 5,15 26,15 26.483,14.128 " }),
|
|
161
179
|
/* @__PURE__ */ a("polygon", { points: "6.583,26.814 5,25.996 5,15 26,15 26.483,15.872 " }),
|
|
162
180
|
/* @__PURE__ */ a("circle", { cx: "26", cy: "15", r: "1" }),
|
|
163
181
|
/* @__PURE__ */ a("circle", { cx: "6", cy: "4", r: "1" }),
|
|
164
182
|
/* @__PURE__ */ a("circle", { cx: "6", cy: "26", r: "1" })
|
|
165
183
|
] }) }),
|
|
166
|
-
/* @__PURE__ */ a("div", { className: "terminal", children: /* @__PURE__ */
|
|
167
|
-
/* @__PURE__ */
|
|
168
|
-
/* @__PURE__ */
|
|
184
|
+
/* @__PURE__ */ a("div", { className: "terminal", children: /* @__PURE__ */ S("div", { className: K(r), style: r.frameBox.style || {}, children: [
|
|
185
|
+
/* @__PURE__ */ S("div", { className: "terminal-titlebar", children: [
|
|
186
|
+
/* @__PURE__ */ S("div", { className: "buttons", children: [
|
|
169
187
|
/* @__PURE__ */ a("div", { className: "close-button" }),
|
|
170
188
|
/* @__PURE__ */ a("div", { className: "minimize-button" }),
|
|
171
189
|
/* @__PURE__ */ a("div", { className: "maximize-button" })
|
|
172
190
|
] }),
|
|
173
|
-
/* @__PURE__ */ a("div", { className: "title", children:
|
|
191
|
+
/* @__PURE__ */ a("div", { className: "title", children: r.frameBox.title || "" })
|
|
174
192
|
] }),
|
|
175
|
-
/* @__PURE__ */ a("div", { className: "terminal-body", children: /* @__PURE__ */ a(
|
|
193
|
+
/* @__PURE__ */ a("div", { className: "terminal-body", children: /* @__PURE__ */ a(Y, { ref: i, options: J }) })
|
|
176
194
|
] }) }),
|
|
177
|
-
/* @__PURE__ */
|
|
178
|
-
!t.isPlaying && t.isStarted && /* @__PURE__ */ a("div", { className: "play", onClick:
|
|
179
|
-
t.isPlaying && /* @__PURE__ */ a("div", { className: "pause", onClick:
|
|
180
|
-
!t.isPlaying && !t.isStarted && /* @__PURE__ */ a("div", { className: "play", onClick:
|
|
181
|
-
/* @__PURE__ */ a("div", { className: "timer", children:
|
|
182
|
-
/* @__PURE__ */ a("div", { className: "progressbar-wrapper", children: /* @__PURE__ */ a("div", { className: "progressbar", ref:
|
|
195
|
+
/* @__PURE__ */ S("div", { className: "controller", children: [
|
|
196
|
+
!t.isPlaying && t.isStarted && /* @__PURE__ */ a("div", { className: "play", onClick: O, title: "Play", children: /* @__PURE__ */ a("span", { className: "icon" }) }),
|
|
197
|
+
t.isPlaying && /* @__PURE__ */ a("div", { className: "pause", onClick: L, title: "Pause", children: /* @__PURE__ */ a("span", { className: "icon" }) }),
|
|
198
|
+
!t.isPlaying && !t.isStarted && /* @__PURE__ */ a("div", { className: "play", onClick: k, title: "Start", children: /* @__PURE__ */ a("span", { className: "icon" }) }),
|
|
199
|
+
/* @__PURE__ */ a("div", { className: "timer", children: Q(t.currentTime) }),
|
|
200
|
+
/* @__PURE__ */ a("div", { className: "progressbar-wrapper", children: /* @__PURE__ */ a("div", { className: "progressbar", ref: N, onClick: M, children: /* @__PURE__ */ a("div", { className: "progress", style: { width: `${t.currentTime / h * 100}%` } }) }) })
|
|
183
201
|
] })
|
|
184
202
|
] });
|
|
185
203
|
}
|
|
186
|
-
|
|
187
|
-
frames:
|
|
188
|
-
options:
|
|
189
|
-
autoplay:
|
|
190
|
-
repeat:
|
|
191
|
-
controls:
|
|
192
|
-
frameBox:
|
|
193
|
-
theme:
|
|
194
|
-
cols:
|
|
195
|
-
rows:
|
|
204
|
+
V.propTypes = {
|
|
205
|
+
frames: l.array.isRequired,
|
|
206
|
+
options: l.shape({
|
|
207
|
+
autoplay: l.bool,
|
|
208
|
+
repeat: l.bool,
|
|
209
|
+
controls: l.bool,
|
|
210
|
+
frameBox: l.object,
|
|
211
|
+
theme: l.object,
|
|
212
|
+
cols: l.number,
|
|
213
|
+
rows: l.number
|
|
196
214
|
}).isRequired,
|
|
197
|
-
onComplete:
|
|
198
|
-
onStart:
|
|
199
|
-
onStop:
|
|
200
|
-
onPause:
|
|
201
|
-
onTick:
|
|
202
|
-
onJump:
|
|
215
|
+
onComplete: l.func,
|
|
216
|
+
onStart: l.func,
|
|
217
|
+
onStop: l.func,
|
|
218
|
+
onPause: l.func,
|
|
219
|
+
onTick: l.func,
|
|
220
|
+
onJump: l.func
|
|
203
221
|
};
|
|
204
222
|
export {
|
|
205
|
-
|
|
206
|
-
|
|
223
|
+
se as PLAYER_FRAME_DELAY,
|
|
224
|
+
V as default
|
|
207
225
|
};
|
package/lib/Terminal.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { jsx as n } from "react/jsx-runtime";
|
|
2
2
|
import h from "react";
|
|
3
|
-
import
|
|
3
|
+
import r from "prop-types";
|
|
4
4
|
import { Terminal as m } from "@xterm/xterm";
|
|
5
5
|
import { WebLinksAddon as a } from "@xterm/addon-web-links";
|
|
6
6
|
import { FitAddon as d } from "@xterm/addon-fit";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import { TerminalRoot as
|
|
10
|
-
class
|
|
7
|
+
import p from "lodash/debounce";
|
|
8
|
+
import o from "lodash/noop";
|
|
9
|
+
import { TerminalRoot as f } from "./styles.js";
|
|
10
|
+
class l extends h.Component {
|
|
11
11
|
xterm = null;
|
|
12
12
|
container = null;
|
|
13
13
|
shouldTriggerFitWhenWrite = !1;
|
|
14
14
|
componentDidMount() {
|
|
15
15
|
const { value: t = "", options: e = {} } = this.props;
|
|
16
|
-
this.fitAddon = new d(), this.xterm = new m(e), this.xterm.loadAddon(new a()), this.xterm.loadAddon(this.fitAddon), this.xterm.open(this.container), this.xterm.onData(this.onData.bind(this)), this.xterm.onRender(this.onRender.bind(this)), t && this.xterm.write(t),
|
|
16
|
+
this.fitAddon = new d(), this.xterm = new m(e), this.xterm.loadAddon(new a()), this.xterm.loadAddon(this.fitAddon), this.xterm.open(this.container), this.xterm.onData(this.onData.bind(this)), this.xterm.onRender(this.onRender.bind(this)), t && this.xterm.write(t), setTimeout(() => {
|
|
17
|
+
this.xterm && this.fitAddon.fit();
|
|
18
|
+
}, 0), this.debounceFit = p(() => {
|
|
17
19
|
this.fitAddon.fit();
|
|
18
20
|
}, 500), window.addEventListener("resize", this.debounceFit);
|
|
19
21
|
}
|
|
@@ -21,8 +23,8 @@ class u extends h.Component {
|
|
|
21
23
|
this.xterm && (this.xterm.dispose(), this.xterm = null), window.removeEventListener("resize", this.debounceFit);
|
|
22
24
|
}
|
|
23
25
|
shouldComponentUpdate(t) {
|
|
24
|
-
const { value: e = "" } = t, { value:
|
|
25
|
-
return t.hasOwnProperty("value") && e !==
|
|
26
|
+
const { value: e = "" } = t, { value: i = "" } = this.props;
|
|
27
|
+
return t.hasOwnProperty("value") && e !== i && this.xterm && (this.xterm.clear(), setTimeout(() => {
|
|
26
28
|
this.xterm.write(e);
|
|
27
29
|
}, 0)), !1;
|
|
28
30
|
}
|
|
@@ -39,20 +41,18 @@ class u extends h.Component {
|
|
|
39
41
|
this.xterm && this.xterm.reset();
|
|
40
42
|
}
|
|
41
43
|
onData = (t) => {
|
|
42
|
-
const { onData: e =
|
|
44
|
+
const { onData: e = o } = this.props;
|
|
43
45
|
e(t);
|
|
44
46
|
};
|
|
45
47
|
onRender = (t) => {
|
|
46
|
-
const { onRender: e =
|
|
48
|
+
const { onRender: e = o } = this.props;
|
|
47
49
|
e(t);
|
|
48
50
|
};
|
|
49
|
-
resize() {
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
this.shouldTriggerFitWhenWrite = !0
|
|
53
|
-
|
|
54
|
-
}, 250);
|
|
55
|
-
}
|
|
51
|
+
resize(t, e) {
|
|
52
|
+
this.xterm && (this.shouldTriggerFitWhenWrite = !0, setTimeout(() => {
|
|
53
|
+
const i = Math.round(t ?? this.props.options.cols), s = Math.round(e ?? this.props.options.rows);
|
|
54
|
+
typeof i == "number" && typeof s == "number" && this.xterm.resize(i, s), this.shouldTriggerFitWhenWrite = !0;
|
|
55
|
+
}, 250));
|
|
56
56
|
}
|
|
57
57
|
setOption(t, e) {
|
|
58
58
|
this.xterm && (this.xterm.options[t] = e);
|
|
@@ -65,27 +65,27 @@ class u extends h.Component {
|
|
|
65
65
|
this.xterm && this.xterm.refresh(0, this.xterm.rows - 1);
|
|
66
66
|
}
|
|
67
67
|
render() {
|
|
68
|
-
const { className: t = "", style: e = {} } = this.props,
|
|
68
|
+
const { className: t = "", style: e = {} } = this.props, i = ["react-xterm", t].filter(Boolean).join(" ");
|
|
69
69
|
return /* @__PURE__ */ n(
|
|
70
|
-
|
|
70
|
+
f,
|
|
71
71
|
{
|
|
72
|
-
ref: (
|
|
73
|
-
this.container =
|
|
72
|
+
ref: (s) => {
|
|
73
|
+
this.container = s;
|
|
74
74
|
},
|
|
75
|
-
className:
|
|
75
|
+
className: i,
|
|
76
76
|
style: e
|
|
77
77
|
}
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
onData:
|
|
83
|
-
onRender:
|
|
84
|
-
options:
|
|
85
|
-
value:
|
|
86
|
-
className:
|
|
87
|
-
style:
|
|
81
|
+
l.propTypes = {
|
|
82
|
+
onData: r.func,
|
|
83
|
+
onRender: r.func,
|
|
84
|
+
options: r.object,
|
|
85
|
+
value: r.string,
|
|
86
|
+
className: r.string,
|
|
87
|
+
style: r.object
|
|
88
88
|
};
|
|
89
89
|
export {
|
|
90
|
-
|
|
90
|
+
l as default
|
|
91
91
|
};
|
package/lib/styles.js
CHANGED
|
@@ -142,17 +142,26 @@ const r = e.div({
|
|
|
142
142
|
"& .xterm .composition-view.active": {
|
|
143
143
|
display: "block"
|
|
144
144
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
145
|
+
"& .xterm .xterm-viewport": {
|
|
146
|
+
// On OS X this is required in order for the scroll bar to appear fully opaque
|
|
147
|
+
backgroundColor: "transparent !important",
|
|
148
|
+
overflowY: "scroll",
|
|
149
|
+
cursor: "default",
|
|
150
|
+
position: "absolute",
|
|
151
|
+
right: 0,
|
|
152
|
+
left: 0,
|
|
153
|
+
top: 0,
|
|
154
|
+
bottom: 0,
|
|
155
|
+
// scrollbarWidth: 'none',
|
|
156
|
+
"&::-webkit-scrollbar": {
|
|
157
|
+
width: 8,
|
|
158
|
+
backgroundColor: "transparent"
|
|
159
|
+
},
|
|
160
|
+
"&::-webkit-scrollbar-thumb": {
|
|
161
|
+
background: "#888",
|
|
162
|
+
borderRadius: 4
|
|
163
|
+
}
|
|
164
|
+
},
|
|
156
165
|
"& .xterm .xterm-screen": {
|
|
157
166
|
position: "relative"
|
|
158
167
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/terminal",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.23",
|
|
4
4
|
"description": "A react wrapper for xterm allowing you to easily render a terminal in the browser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^19.0.0"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "2e5df6543f1155269b51fd833e37b64ca958029f",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@arcblock/react-hooks": "3.1.
|
|
46
|
-
"@arcblock/ux": "3.1.
|
|
45
|
+
"@arcblock/react-hooks": "3.1.23",
|
|
46
|
+
"@arcblock/ux": "3.1.23",
|
|
47
47
|
"@emotion/react": "^11.14.0",
|
|
48
48
|
"@emotion/styled": "^11.14.0",
|
|
49
49
|
"@xterm/addon-fit": "^0.10.0",
|
package/src/Player.jsx
CHANGED
|
@@ -85,6 +85,7 @@ export default function Player({ ...rawProps }) {
|
|
|
85
85
|
const container = useRef(null);
|
|
86
86
|
const animationRef = useRef(null);
|
|
87
87
|
const startTimeRef = useRef(null);
|
|
88
|
+
const autoPlayRef = useRef(false);
|
|
88
89
|
const [state, dispatch] = useReducer(stateReducer, defaultState);
|
|
89
90
|
|
|
90
91
|
// Render a frame with Promise-based approach
|
|
@@ -234,6 +235,38 @@ export default function Player({ ...rawProps }) {
|
|
|
234
235
|
return false;
|
|
235
236
|
}, [emitEvent]);
|
|
236
237
|
|
|
238
|
+
// Intersection Observer for viewport detection
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
if (!container.current || !options.autoplay || autoPlayRef.current) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const observer = new IntersectionObserver(
|
|
245
|
+
(entries) => {
|
|
246
|
+
const [entry] = entries;
|
|
247
|
+
if (entry.isIntersecting && !state.isStarted) {
|
|
248
|
+
// 根据视野范围自动开始播放
|
|
249
|
+
onStart();
|
|
250
|
+
autoPlayRef.current = true;
|
|
251
|
+
// 播放后,断开 observer,避免触发多次
|
|
252
|
+
observer.unobserve(container.current);
|
|
253
|
+
observer.disconnect();
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
threshold: 0.1, // Trigger when 10% of the element is visible
|
|
258
|
+
rootMargin: '0px 0px 0px 0px', // Add some margin to avoid triggering too early
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
observer.observe(container.current);
|
|
263
|
+
|
|
264
|
+
// eslint-disable-next-line consistent-return
|
|
265
|
+
return () => {
|
|
266
|
+
observer.disconnect();
|
|
267
|
+
};
|
|
268
|
+
}, [options.autoplay, state.isStarted, onStart]);
|
|
269
|
+
|
|
237
270
|
// Render thumbnailTime
|
|
238
271
|
useEffect(() => {
|
|
239
272
|
if (!terminal.current) {
|
|
@@ -243,9 +276,7 @@ export default function Player({ ...rawProps }) {
|
|
|
243
276
|
// Focus terminal to ensure cursor is visible
|
|
244
277
|
terminal.current.focus();
|
|
245
278
|
|
|
246
|
-
if (options.autoplay) {
|
|
247
|
-
onStart();
|
|
248
|
-
} else {
|
|
279
|
+
if (!options.autoplay) {
|
|
249
280
|
doJump(Math.min(Math.abs(options.thumbnailTime), totalDuration));
|
|
250
281
|
}
|
|
251
282
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
package/src/Player.stories.jsx
CHANGED
|
@@ -3,6 +3,7 @@ export { default as AutoPlay } from './demo/autoplay';
|
|
|
3
3
|
export { default as Loop } from './demo/loop';
|
|
4
4
|
export { default as AutoPlayLoop } from './demo/autoplay-loop';
|
|
5
5
|
export { default as RecordingGuide } from './demo/recording-guide';
|
|
6
|
+
export { default as BlockletLogTerminal } from './demo/blocklet-log-terminal';
|
|
6
7
|
|
|
7
8
|
export default {
|
|
8
9
|
title: 'Data Display/Terminal/Player',
|
package/src/Terminal.jsx
CHANGED
|
@@ -33,6 +33,12 @@ export default class Terminal extends React.Component {
|
|
|
33
33
|
this.xterm.write(value);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
if (this.xterm) {
|
|
38
|
+
this.fitAddon.fit();
|
|
39
|
+
}
|
|
40
|
+
}, 0);
|
|
41
|
+
|
|
36
42
|
this.debounceFit = debounce(() => {
|
|
37
43
|
this.fitAddon.fit();
|
|
38
44
|
}, 500);
|
|
@@ -100,12 +106,15 @@ export default class Terminal extends React.Component {
|
|
|
100
106
|
onRender(data);
|
|
101
107
|
};
|
|
102
108
|
|
|
103
|
-
resize() {
|
|
109
|
+
resize(_cols, _rows) {
|
|
104
110
|
if (this.xterm) {
|
|
105
|
-
const { cols, rows } = this.props.options;
|
|
106
111
|
this.shouldTriggerFitWhenWrite = true;
|
|
107
112
|
setTimeout(() => {
|
|
108
|
-
|
|
113
|
+
const cols = Math.round(_cols ?? this.props.options.cols);
|
|
114
|
+
const rows = Math.round(_rows ?? this.props.options.rows);
|
|
115
|
+
if (typeof cols === 'number' && typeof rows === 'number') {
|
|
116
|
+
this.xterm.resize(cols, rows);
|
|
117
|
+
}
|
|
109
118
|
this.shouldTriggerFitWhenWrite = true;
|
|
110
119
|
}, 250);
|
|
111
120
|
}
|
package/src/styles.js
CHANGED
|
@@ -174,17 +174,27 @@ export const TerminalRoot = styled.div({
|
|
|
174
174
|
display: 'block',
|
|
175
175
|
},
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
177
|
+
'& .xterm .xterm-viewport': {
|
|
178
|
+
// On OS X this is required in order for the scroll bar to appear fully opaque
|
|
179
|
+
backgroundColor: 'transparent !important',
|
|
180
|
+
overflowY: 'scroll',
|
|
181
|
+
cursor: 'default',
|
|
182
|
+
position: 'absolute',
|
|
183
|
+
right: 0,
|
|
184
|
+
left: 0,
|
|
185
|
+
top: 0,
|
|
186
|
+
bottom: 0,
|
|
187
|
+
// scrollbarWidth: 'none',
|
|
188
|
+
'&::-webkit-scrollbar': {
|
|
189
|
+
width: 8 /* 滚动条宽度 */,
|
|
190
|
+
backgroundColor: 'transparent' /* 滚动条背景 */,
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
'&::-webkit-scrollbar-thumb': {
|
|
194
|
+
background: '#888' /* 滚动条滑块颜色 */,
|
|
195
|
+
borderRadius: 4 /* 滑块圆角 */,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
188
198
|
|
|
189
199
|
'& .xterm .xterm-screen': {
|
|
190
200
|
position: 'relative',
|