Skip to content
This repository was archived by the owner on Jul 30, 2025. It is now read-only.

Commit 0c33e6e

Browse files
myan9starpit
authored andcommitted
feat: add capability to show welcome widget to new users in Terminal
Fixes #4990 Fixes #5007
1 parent 246ecd2 commit 0c33e6e

File tree

18 files changed

+199
-111
lines changed

18 files changed

+199
-111
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565

6666
- env: N="API" CLIENT="test" MOCHA_RUN_TARGET="webpack" LAYERS="response" CODECOV_OF_PRESCAN=true
6767
- env: N="API" CLIENT="test" MOCHA_RUN_TARGET="electron" LAYERS="response"
68-
- env: N="bottom-input" CLIENT="alternate" MOCHA_RUN_TARGET="webpack" LAYERS="bottom-input"
68+
- env: N="bottom-input" CLIENT="alternate" MOCHA_RUN_TARGET="webpack" LAYERS="bottom-input" BOTTOM_INPUT_MODE=true
6969

7070
# CLIENT=default | os=Darwin | TARGET=electron | core, core-support, editor, core-support2
7171
- os: osx

packages/test/src/api/cli.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ export const command = async (
6868
if (process.env.BOTTOM_INPUT_MODE) await app.client.waitForExist(Selectors.BOTTOM_PROMPT_BLOCK, timeout - 5000)
6969
if (!noFocus) return grabFocus(app)
7070
})
71-
.then(() => app.client.getAttribute(block, 'data-input-count'))
71+
.then(() =>
72+
app.client.getAttribute(process.env.BOTTOM_INPUT_MODE ? Selectors.BOTTOM_PROMPT_BLOCK : block, 'data-input-count')
73+
)
7274
.then(async count => {
7375
if (!noCopyPaste && cmd.length > 1) {
7476
// use the clipboard for a fast path
@@ -120,6 +122,7 @@ export const waitForSession = async (ctx: Common.ISuite, noProxySessionWait = fa
120122
// wait for the proxy session to be established
121123
try {
122124
await ctx.app.client.waitForExist(`${Selectors.CURRENT_TAB}.kui--session-init-done`)
125+
await ctx.app.client.waitForVisible(Selectors.WELCOME_BLOCK)
123126
} catch (err) {
124127
throw new Error('error waiting for proxy session init')
125128
}

packages/test/src/api/repl-expect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export const blank = (res: AppAndCount) => blankWithOpts()(res)
172172
export const consoleToBeClear = (app: Application) => {
173173
return app.client.waitUntil(async () => {
174174
return app.client.elements(Selectors.PROMPT_BLOCK).then(elements => elements.value.length === 1)
175-
})
175+
}, waitTimeout)
176176
}
177177

178178
/** as long as its ok, accept anything */

packages/test/src/api/selectors.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const SIDECAR_FULLSCREEN = `${CURRENT_TAB} .kui--sidecar.visible.maximize
88
export const TERMINAL_WITH_SIDECAR_VISIBLE = `${CURRENT_TAB} .repl.sidecar-visible`
99
const _PROMPT_BLOCK = '.repl-block'
1010
export const PROMPT_BLOCK = `${CURRENT_TAB} .repl ${_PROMPT_BLOCK}`
11+
export const WELCOME_BLOCK = `${PROMPT_BLOCK} .kui--repl-message.kui--session-init-done`
1112
export const BOTTOM_PROMPT_BLOCK = `${CURRENT_TAB} .kui--input-stripe .repl-block`
1213
export const BOTTOM_PROMPT = `${BOTTOM_PROMPT_BLOCK} input`
1314
export const STATUS_STRIPE_BLOCK = '.kui--status-stripe .kui--input-stripe .repl-block'
@@ -103,8 +104,8 @@ export const OUTPUT_N_PTY = (N: number) => OUTPUT_N_STREAMING(N)
103104
export const PROMPT_BLOCK_LAST = `${PROMPT_BLOCK}:nth-last-child(2)`
104105
export const PROMPT_BLOCK_FINAL = `${PROMPT_BLOCK}:nth-last-child(1)`
105106
export const OVERFLOW_MENU = '.kui--repl-block-right-element.kui--toolbar-button-with-icon'
106-
export const PROMPT_BLOCK_LAST_MENU = `${PROMPT_BLOCK_LAST} ${OVERFLOW_MENU}`
107-
export const BLOCK_REMOVE_BUTTON = `${OVERFLOW_MENU} button[data-mode="Remove"]`
107+
export const PROMPT_BLOCK_MENU = (N: number) => `${PROMPT_BLOCK_N(N)} ${OVERFLOW_MENU}`
108+
export const BLOCK_REMOVE_BUTTON = `${OVERFLOW_MENU} button[data-mode="Remove"]` // in carbon, this is a global
108109
export const PROMPT_LAST = `${PROMPT_BLOCK_LAST} .repl-input-element`
109110
export const PROMPT_FINAL = `${PROMPT_BLOCK_FINAL} .repl-input-element`
110111
export const OUTPUT_LAST = `${PROMPT_BLOCK_LAST} .repl-result`

plugins/plugin-bash-like/web/scss/xterm.scss

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@import "~xterm/css/xterm.css";
1+
@import '~xterm/css/xterm.css';
22

33
/* Explanation for width hacks:
44
see https://github.com/xtermjs/xterm.js/pull/2067
@@ -39,8 +39,10 @@ disabled see https://github.com/IBM/kui/issues/3939
3939
height: auto !important;
4040
}
4141

42-
/* alt buffer mode */
43-
&.xterm-alt-buffer-mode, .xterm-alt-buffer-mode { /* no-splitTerminal and splitTerminal variants */
42+
/* alt buffer mode */
43+
&.visible.xterm-alt-buffer-mode,
44+
.xterm-alt-buffer-mode {
45+
/* no-splitTerminal and splitTerminal variants */
4446
.xterm-container .xterm-rows.xterm-focus .xterm-cursor.xterm-cursor-block {
4547
background-color: var(--color-base08);
4648
color: var(--color-base00);
@@ -265,7 +267,7 @@ disabled see https://github.com/IBM/kui/issues/3939
265267
.xterm-is-wrapped-with-prefix-break:before {
266268
/* see https://github.com/IBM/kui/issues/1605 */
267269
display: block;
268-
content: "";
270+
content: '';
269271

270272
/* see https://github.com/IBM/kui/issues/2681 */
271273
height: 0;
@@ -287,6 +289,6 @@ disabled see https://github.com/IBM/kui/issues/3939
287289
}
288290

289291
/* selection */
290-
[kui-theme-style="light"] .xterm-container .xterm-screen .xterm-selection div {
292+
[kui-theme-style='light'] .xterm-container .xterm-screen .xterm-selection div {
291293
background-color: rgba(0, 0, 0, 0.3);
292294
}

plugins/plugin-client-alternate/src/test/bottom-input/new-tab.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,24 @@ describe('core new tab switch tabs', function(this: Common.ISuite) {
2626
CLI.command('tab new', this.app)
2727
.then(() => this.app.client.waitForVisible(Selectors.TAB_SELECTED_N(2)))
2828
.then(() => CLI.waitForSession(this)) // should have an active repl
29-
.catch(Common.oops(this)))
29+
.catch(Common.oops(this, true)))
3030

3131
it(`switch back to first tab via command`, () =>
3232
CLI.command('tab switch 1', this.app)
3333
.then(() => this.app.client.waitForVisible(Selectors.TAB_SELECTED_N(1)))
34-
.catch(Common.oops(this)))
34+
.catch(Common.oops(this, true)))
3535

3636
it(`switch back to second tab via command`, () =>
3737
CLI.command('tab switch 2', this.app)
3838
.then(() => this.app.client.waitForVisible(Selectors.TAB_SELECTED_N(2)))
39-
.catch(Common.oops(this)))
39+
.catch(Common.oops(this, true)))
4040

4141
it('should close tab via "tab close" command', () =>
4242
CLI.command('tab close', this.app)
4343
.then(() => this.app.client.waitForExist(Selectors.TAB_N(2), 5000, true))
4444
.then(() => this.app.client.waitForVisible(Selectors.TAB_SELECTED_N(1)))
4545
.then(() => CLI.waitForRepl(this.app)) // should have an active repl
46-
.catch(Common.oops(this)))
46+
.catch(Common.oops(this, true)))
4747
})
4848

4949
describe('core new tab from pty active tab via button click', function(this: Common.ISuite) {
@@ -56,11 +56,11 @@ describe('core new tab from pty active tab via button click', function(this: Com
5656
.then(() => this.app.client.click(tabButtonSelector))
5757
.then(() => this.app.client.waitForVisible(Selectors.TAB_SELECTED_N(2)))
5858
.then(() => CLI.waitForSession(this)) // should have an active repl
59-
.catch(Common.oops(this)))
59+
.catch(Common.oops(this, true)))
6060

6161
it('should report proper version', () =>
6262
CLI.command('version', this.app)
6363
.then(ReplExpect.okWithCustom({ expect: Common.expectedVersion }))
6464
.then(() => this.app.client.waitForVisible(Selectors.TAB_SELECTED_N(2)))
65-
.catch(Common.oops(this)))
65+
.catch(Common.oops(this, true)))
6666
})

plugins/plugin-client-common/src/components/Client/Kui.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ export class Kui extends React.PureComponent<Props, State> {
119119

120120
private defaultFeatureFlag() {
121121
return {
122-
sidecarName: 'breadcrumb'
122+
sidecarName: 'breadcrumb',
123+
showWelcomeMax: -1
123124
}
124125
}
125126

plugins/plugin-client-common/src/components/Client/TabContent.tsx

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -212,23 +212,6 @@ export default class TabContent extends React.PureComponent<Props, State> {
212212
return <Loading description={strings('Please wait while we connect to your cloud')} />
213213
}
214214

215-
private sessionInitDoneMessage() {
216-
return (
217-
this.state.showSessionInitDone &&
218-
this.state.sidecarWidth === Width.Closed && (
219-
<KuiContext.Consumer>
220-
{config =>
221-
config.loadingDone && (
222-
<div className="kui--repl-message kui--session-init-done">
223-
<span className="repl-block">{config.loadingDone(this.state.tab.REPL)}</span>
224-
</div>
225-
)
226-
}
227-
</KuiContext.Consumer>
228-
)
229-
)
230-
}
231-
232215
private terminal() {
233216
if (this.state.sessionInit !== 'Done') {
234217
return (
@@ -247,7 +230,6 @@ export default class TabContent extends React.PureComponent<Props, State> {
247230
} else {
248231
return (
249232
<React.Fragment>
250-
{this.sessionInitDoneMessage()}
251233
<KuiContext.Consumer>
252234
{config => (
253235
<ScrollableTerminal

plugins/plugin-client-common/src/components/Client/props/FeatureFlags.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ type FeatureFlags = {
2929

3030
/** [Optional] automatically pin watchable command ouptut to the WatchPane? */
3131
enableWatcherAutoPin?: boolean
32+
33+
/**
34+
* [Optional] maximum number of times to show `loadingDone` to users
35+
* if set to -1, always show welcome;
36+
* if not 0, not show welcome
37+
* default: -1
38+
*
39+
*/
40+
showWelcomeMax?: number
3241
}
3342

3443
export default FeatureFlags

plugins/plugin-client-common/src/components/Views/Terminal/Block/BlockModel.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,14 @@ type WithState<S extends BlockState> = { state: S }
3535
type WithResponse<R extends ScalarResponse> = { response: R } & WithStartTime
3636
type WithValue = { value: string }
3737
type withPin = { isPinned: boolean }
38+
type WithAnnouncement = { isAnnouncement: boolean }
3839

3940
/** The canonical types of Blocks, which mix up the Traits as needed */
4041
type ActiveBlock = WithState<BlockState.Active> & WithCWD & Partial<WithValue>
42+
export type AnnouncementBlock = WithState<BlockState.ValidResponse> &
43+
WithResponse<ScalarResponse> &
44+
WithCWD &
45+
WithAnnouncement
4146
type EmptyBlock = WithState<BlockState.Empty> & WithCWD
4247
type ErrorBlock = WithState<BlockState.Error> & WithCommand & WithResponse<Error> & WithUUID
4348
type OkBlock = WithState<BlockState.ValidResponse> & WithCommand & WithResponse<ScalarResponse> & WithUUID
@@ -48,7 +53,8 @@ type CancelledBlock = WithState<BlockState.Cancelled> & WithCWD & WithCommand &
4853
export type FinishedBlock = OkBlock | ErrorBlock | CancelledBlock | EmptyBlock
4954

5055
// A Block is one of the canonical types
51-
export type BlockModel = (ProcessingBlock | FinishedBlock | CancelledBlock | ActiveBlock) & Partial<withPin>
56+
export type BlockModel = (ProcessingBlock | FinishedBlock | CancelledBlock | ActiveBlock | AnnouncementBlock) &
57+
Partial<withPin>
5258
export default BlockModel
5359

5460
/** Capture the current working directory */
@@ -93,8 +99,13 @@ export function hasCommand(block: BlockModel & Partial<WithCommand>): block is B
9399
return !isActive(block) && !isEmpty(block)
94100
}
95101

102+
export function isAnnouncement(block: BlockModel): block is AnnouncementBlock {
103+
const blockModel = block as AnnouncementBlock
104+
return blockModel.state === BlockState.ValidResponse && blockModel.isAnnouncement === true
105+
}
106+
96107
export function hasUUID(block: BlockModel & Partial<WithUUID>): block is BlockModel & Required<WithUUID> {
97-
return !isActive(block) && !isEmpty(block)
108+
return !isActive(block) && !isEmpty(block) && !isAnnouncement(block)
98109
}
99110

100111
export function hasValue(block: BlockModel): block is BlockModel & Required<WithValue> {
@@ -110,6 +121,17 @@ export function Active(initialValue?: string): ActiveBlock {
110121
}
111122
}
112123

124+
/** Transform to AnnouncementBlock */
125+
export function Announcement(response: ScalarResponse): AnnouncementBlock {
126+
return {
127+
response,
128+
isAnnouncement: true,
129+
startTime: new Date(),
130+
cwd: cwd(),
131+
state: BlockState.ValidResponse
132+
}
133+
}
134+
113135
/** Transform to Processing */
114136
export function Processing(block: BlockModel, command: string, execUUID: string): ProcessingBlock {
115137
return {

0 commit comments

Comments
 (0)