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

Commit 38d2895

Browse files
committed
feat: inverse splits
Fixes #5537
1 parent 8039a9d commit 38d2895

File tree

10 files changed

+70
-39
lines changed

10 files changed

+70
-39
lines changed

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,17 +258,21 @@ export const okWith = (entityName: string) => async (res: AppAndCount) => expect
258258
/** expect just ok, and no result value */
259259
export const justOK = async (res: AppAndCount) => expectOK(res, { expectJustOK: true }).then(() => res.app)
260260

261-
/** Expect the given number of terminal splits in the current tab */
262-
export function splitCount(expectedSplitCount: number) {
263-
return (app: Application) => {
261+
/** Expect the given number of terminal splits in the current tab, and check whether the last split has inverse colors */
262+
export function splitCount(expectedSplitCount: number, inverseColors = false) {
263+
return async (app: Application) => {
264264
let idx = 0
265-
return app.client.waitUntil(async () => {
265+
await app.client.waitUntil(async () => {
266266
const { value } = await app.client.elements(Selectors.SPLITS)
267267
if (++idx > 5) {
268268
console.error(`still waiting for splitCount; actual=${value.length} expected=${expectedSplitCount}`)
269269
}
270270
return value.length === expectedSplitCount
271271
}, waitTimeout)
272+
273+
if (inverseColors) {
274+
await app.client.waitForExist(Selectors.SPLIT_N(expectedSplitCount, inverseColors))
275+
}
272276
}
273277
}
274278

packages/test/src/api/selectors.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,16 @@ export const PROCESSING_N = (N: number) => `${PROMPT_BLOCK_N(N)}.processing`
101101
const _PROMPT = '.repl-input-element'
102102
export const CURRENT_PROMPT = `${CURRENT_PROMPT_BLOCK} ${_PROMPT}`
103103

104+
export const INVERTED_COLORS = '.kui--inverted-color-context'
105+
104106
/**
105107
* Terminal splits
106108
*
107109
*/
108110
export const NEW_SPLIT_BUTTON = '#kui--split-terminal-button'
109111
export const SPLITS = `${CURRENT_TAB} .kui--scrollback`
110-
export const SPLIT_N = (N: number) => `${SPLITS}:nth-child(${N})`
112+
export const SPLIT_N = (N: number, inverseColors = false) =>
113+
`${SPLITS}:nth-child(${N})` + (inverseColors ? INVERTED_COLORS : '')
111114
export const SPLIT_N_FOCUS = (N: number) => `${SPLITS}:nth-child(${N}) .repl-input`
112115
export const SPLIT_N_OUTPUT = (N: number) => `${SPLITS}:nth-child(${N}) .repl-output`
113116
export const CURRENT_PROMPT_BLOCK_FOR_SPLIT = (splitIndex: number) => `${SPLIT_N(splitIndex)} ${current(_PROMPT_BLOCK)}`

plugins/plugin-client-common/i18n/resources_en_US.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"Show X": "Show {0}",
1313
"Hide X": "Hide {0}",
1414
"New Tab": "Open a new tab",
15+
"Created a split": "Created a split",
16+
"Created a split with inverted colors": "Created a split with inverted colors",
1517
"Duration": "Duration: {0}",
1618
"Cold Start": "Cold start delay: {0}. {1}",
1719
"Close this tab": "Close this tab",

plugins/plugin-client-common/src/components/Views/Terminal/ScrollableTerminal.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import KuiConfiguration from '../../Client/KuiConfiguration'
4040
import {
4141
Active,
4242
Finished,
43-
FinishedBlock,
4443
Announcement,
4544
Cancelled,
4645
Processing,
@@ -97,7 +96,12 @@ type Props = TerminalOptions & {
9796
closeSidecar: () => void
9897
}
9998

100-
interface ScrollbackState {
99+
interface ScrollbackOptions {
100+
/** use inverse colors in this split? */
101+
inverseColors?: boolean
102+
}
103+
104+
type ScrollbackState = ScrollbackOptions & {
101105
uuid: string
102106
blocks: BlockModel[]
103107
nAnnouncements: number
@@ -128,10 +132,10 @@ interface State {
128132
splits: ScrollbackState[]
129133
}
130134

131-
function doSplitViewViaId(uuid: string, focusBlock?: FinishedBlock) {
135+
function doSplitViewViaId(uuid: string, opts?: ScrollbackOptions) {
132136
const res = new Promise((resolve, reject) => {
133137
const requestChannel = `/kui-shell/TabContent/v1/tab/${uuid}`
134-
setTimeout(() => eventChannelUnsafe.emit(requestChannel, resolve, reject, focusBlock))
138+
setTimeout(() => eventChannelUnsafe.emit(requestChannel, resolve, reject, opts))
135139
}).catch(err => {
136140
console.error('doSplitViewViaId', err)
137141
throw err
@@ -140,12 +144,12 @@ function doSplitViewViaId(uuid: string, focusBlock?: FinishedBlock) {
140144
}
141145

142146
/** Split the given tab uuid */
143-
export function doSplitView(tab: KuiTab) {
147+
export function doSplitView(tab: KuiTab, opts?: ScrollbackOptions) {
144148
const uuid = isScrollback(tab) ? tab.uuid : tab.querySelector('.kui--scrollback').getAttribute('data-scrollback-id')
145-
return doSplitViewViaId(uuid)
149+
return doSplitViewViaId(uuid, opts)
146150
}
147151

148-
type SplitHandler = (resolve: (response: true) => void, reject: (err: Error) => void) => void
152+
type SplitHandler = (resolve: (response: true) => void, reject: (err: Error) => void, opts?: ScrollbackOptions) => void
149153

150154
function onSplit(uuid: string, handler: SplitHandler) {
151155
const requestChannel = `/kui-shell/TabContent/v1/tab/${uuid}`
@@ -261,12 +265,17 @@ export default class ScrollableTerminal extends React.PureComponent<Props, State
261265
return []
262266
}
263267

264-
private scrollback(capturedValue?: string, sbuuid = this.allocateUUIDForScrollback()): ScrollbackState {
268+
private scrollback(
269+
capturedValue?: string,
270+
sbuuid = this.allocateUUIDForScrollback(),
271+
opts: ScrollbackOptions = {}
272+
): ScrollbackState {
265273
const state: ScrollbackState = {
266274
uuid: sbuuid,
267275
cleaners: [],
268276
forceMiniSplit: false,
269277
nAnnouncements: 0,
278+
inverseColors: opts.inverseColors,
270279
showThisIdxInMiniSplit: -2,
271280
blocks: (capturedValue !== undefined ? [] : this.restoreBlocks(sbuuid)).concat([Active(capturedValue)])
272281
}
@@ -520,15 +529,15 @@ export default class ScrollableTerminal extends React.PureComponent<Props, State
520529
}
521530

522531
/** Split the view */
523-
private onSplit(resolve: (response: string) => void, reject: (err: Error) => void) {
532+
private onSplit(resolve: (response: string) => void, reject: (err: Error) => void, opts?: ScrollbackOptions) {
524533
const nTerminals = this.state.splits.length
525534

526535
if (nTerminals === MAX_TERMINALS) {
527536
reject(new Error(strings('No more splits allowed')))
528537
} else {
529538
eventBus.emitTabLayoutChange(this.props.tab.uuid)
530539

531-
const newScrollback = this.scrollback()
540+
const newScrollback = this.scrollback(undefined, undefined, opts)
532541
this.setState(({ splits, focusedIdx }) => {
533542
const newFocus = focusedIdx + 1
534543
const newSplits = splits
@@ -798,7 +807,9 @@ export default class ScrollableTerminal extends React.PureComponent<Props, State
798807
return React.createElement(
799808
'div',
800809
{
801-
className: 'kui--scrollback scrollable scrollable-auto',
810+
className:
811+
'kui--scrollback scrollable scrollable-auto' +
812+
(scrollback.inverseColors ? ' kui--inverted-color-context' : ''),
802813
'data-is-minisplit': isMiniSplit,
803814
'data-is-width-constrained': isWidthConstrained || undefined,
804815
key: tab.uuid,

plugins/plugin-client-common/src/controller/split.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Arguments, ParsedOptions, getCurrentTab } from '@kui-shell/core'
17+
import { Arguments, ParsedOptions, getCurrentTab, i18n } from '@kui-shell/core'
18+
19+
const strings = i18n('plugin-client-common')
1820

1921
interface Options extends ParsedOptions {
2022
debug: boolean
23+
inverse: boolean
2124
}
2225

2326
/**
@@ -31,14 +34,17 @@ export default async function split(args?: Arguments<Options>) {
3134
}
3235

3336
const { doSplitView } = await import('../components/Views/Terminal/ScrollableTerminal')
34-
const tabUUID = await doSplitView(args ? args.tab : getCurrentTab())
37+
38+
const opts = args.parsedOptions.inverse ? { inverseColors: true } : undefined
39+
const tabUUID = await doSplitView(args ? args.tab : getCurrentTab(), opts)
40+
3541
return {
3642
apiVersion: 'kui-shell/v1',
3743
kind: 'CommentaryResponse',
3844
props: {
3945
elsewhere: true,
4046
tabUUID,
41-
children: 'Created a new split'
47+
children: strings(args.parsedOptions.inverse ? 'Created a split with inverted colors' : 'Created a split')
4248
}
4349
}
4450
}

plugins/plugin-client-common/src/plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default async (registrar: Registrar) => {
2020
if (!isHeadless()) {
2121
await import(/* webpackMode: "lazy" */ './controller/confirm').then(_ => _.default(registrar))
2222
await import(/* webpackMode: "lazy" */ './controller/split').then(_ =>
23-
registrar.listen('/split', _.default, { outputOnly: true })
23+
registrar.listen('/split', _.default, { outputOnly: true, flags: { boolean: ['debug', 'inverse'] } })
2424
)
2525
await import(/* webpackMode: "lazy" */ './controller/alert').then(_ => _.default(registrar))
2626
await import(/* webpackMode: "lazy" */ './controller/card').then(_ => _.default(registrar))

plugins/plugin-client-notebook/tutorials/welcome.json

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"apiVersion": "kui-shell/v1",
33
"kind": "Snapshot",
44
"spec": {
5-
"title": true,
5+
"title": "Welcome to Kui",
66
"windows": [
77
{
88
"uuid": "0",
@@ -63,7 +63,7 @@
6363
"startEvent": {
6464
"tab": "3_c57e811f-2da8-557e-a402-9f0950407675",
6565
"route": "/split",
66-
"command": "split",
66+
"command": "split --inverse",
6767
"evaluatorOptions": {
6868
"outputOnly": true,
6969
"plugin": "plugin-client-common"
@@ -74,9 +74,10 @@
7474
"completeEvent": {
7575
"tab": "3_c57e811f-2da8-557e-a402-9f0950407675",
7676
"execType": 0,
77-
"command": "split",
77+
"command": "split --inverse",
7878
"argvNoOptions": ["split"],
7979
"parsedOptions": {
80+
"inverse": true,
8081
"_": ["split"]
8182
},
8283
"execUUID": "45576d3b-3a21-4120-a358-bc2801cfdcb1",
@@ -111,7 +112,7 @@
111112
"startEvent": {
112113
"tab": "3_c57e811f-2da8-557e-a402-9f0950407675",
113114
"route": "/#",
114-
"command": "# # Notebooks with Kui!\\nUsing Kui, you may now craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application.",
115+
"command": "# # Notebooks with Kui!\\nUsing Kui, you may craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application.",
115116
"evaluatorOptions": {
116117
"usage": {
117118
"command": "commentary",
@@ -140,15 +141,15 @@
140141
"completeEvent": {
141142
"tab": "3_c57e811f-2da8-557e-a402-9f0950407675",
142143
"execType": 0,
143-
"command": "# # Notebooks with Kui!\\nUsing Kui, you may now craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application.",
144+
"command": "# # Notebooks with Kui!\\nUsing Kui, you may craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application.",
144145
"argvNoOptions": [
145146
"#",
146-
" # Notebooks with Kui!\\nUsing Kui, you may now craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application."
147+
" # Notebooks with Kui!\\nUsing Kui, you may craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application."
147148
],
148149
"parsedOptions": {
149150
"_": [
150151
"#",
151-
" # Notebooks with Kui!\\nUsing Kui, you may now craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application."
152+
" # Notebooks with Kui!\\nUsing Kui, you may craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application."
152153
]
153154
},
154155
"execUUID": "c0d2c1cb-2274-4509-8af2-60f39efb924e",
@@ -169,7 +170,7 @@
169170
"apiVersion": "kui-shell/v1",
170171
"kind": "CommentaryResponse",
171172
"props": {
172-
"children": "# Notebooks with Kui!\nUsing Kui, you may now craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application."
173+
"children": "# Notebooks with Kui!\nUsing Kui, you may craft notebooks that intermix commentary (such as this) with command executions. The notebooks preserve the output of commands, so that you may share a full session with others. You can even serve up notebooks with a purely static single page web application."
173174
}
174175
},
175176
"responseType": "ScalarResponse",
@@ -388,7 +389,7 @@
388389
"startEvent": {
389390
"tab": "3_0a9586b6-2f57-5d3b-9619-b5cdb6a76d53",
390391
"route": "/#",
391-
"command": "# If you would like to contribute to Kui, either by contributing new Notebooks or new core features, join us!\\n\\nhttps://github.com/IBM/kui",
392+
"command": "# If you would like to contribute to Kui, either by crafting new Notebooks or coding new core features, join us!\\n\\nhttps://github.com/IBM/kui",
392393
"evaluatorOptions": {
393394
"usage": {
394395
"command": "commentary",
@@ -417,15 +418,15 @@
417418
"completeEvent": {
418419
"tab": "3_0a9586b6-2f57-5d3b-9619-b5cdb6a76d53",
419420
"execType": 0,
420-
"command": "# If you would like to contribute to Kui, either by contributing new Notebooks or new core features, join us!\\n\\nhttps://github.com/IBM/kui",
421+
"command": "# If you would like to contribute to Kui, either by crafting new Notebooks or coding new core features, join us!\\n\\nhttps://github.com/IBM/kui",
421422
"argvNoOptions": [
422423
"#",
423-
" If you would like to contribute to Kui, either by contributing new Notebooks or new core features, join us!\\n\\nhttps://github.com/IBM/kui"
424+
" If you would like to contribute to Kui, either by crafting new Notebooks or coding new core features, join us!\\n\\nhttps://github.com/IBM/kui"
424425
],
425426
"parsedOptions": {
426427
"_": [
427428
"#",
428-
" If you would like to contribute to Kui, either by contributing new Notebooks or new core features, join us!\\n\\nhttps://github.com/IBM/kui"
429+
" If you would like to contribute to Kui, either by crafting new Notebooks or coding new core features, join us!\\n\\nhttps://github.com/IBM/kui"
429430
]
430431
},
431432
"execUUID": "8213e543-6b21-47ed-98ae-c5df3d3457a1",

plugins/plugin-core-support/src/lib/cmds/replay.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,8 @@ export default function(registrar: Registrar) {
354354
// }
355355

356356
// NOTE: work around the replayability issue of split command not returning a replayable model: https://github.com/IBM/kui/issues/5399
357-
if (startEvent.command === 'split') {
358-
const ourUUID = (await REPL.qexec<ElsewhereCommentaryResponse>('split')).props.tabUUID
357+
if (startEvent.route === '/split') {
358+
const ourUUID = (await REPL.qexec<ElsewhereCommentaryResponse>(startEvent.command)).props.tabUUID
359359
const theirUUID = (completeEvent.response as ElsewhereCommentaryResponse).props.tabUUID
360360
splitAlignment[theirUUID] = ourUUID
361361
} else if (!splitAlignment[startEvent.tab]) {

plugins/plugin-core-support/src/test/core-support2/split-helpers.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@ export function splitViaButton(this: Common.ISuite, splitCount: number) {
3535
}
3636

3737
/** Split the terminal in the current tab by using the "split" command */
38-
export function splitViaCommand(this: Common.ISuite, splitCount: number, expectErr = false) {
38+
export function splitViaCommand(this: Common.ISuite, splitCount: number, expectErr = false, inverseColors = false) {
3939
it(`should split the terminal via command in the current tab and expect splitCount=${splitCount}`, () => {
40-
return CLI.commandInSplit('split', this.app, splitCount - 1)
41-
.then(expectErr ? ReplExpect.error(500) : ReplExpect.elsewhere('Created a new split'))
42-
.then(ReplExpect.splitCount(splitCount))
40+
return CLI.commandInSplit(`split ${inverseColors ? ' --inverse' : ''}`, this.app, splitCount - 1)
41+
.then(
42+
expectErr
43+
? ReplExpect.error(500)
44+
: ReplExpect.elsewhere(inverseColors ? 'Created a split with inverted colors' : 'Created a split')
45+
)
46+
.then(ReplExpect.splitCount(splitCount, inverseColors))
4347
.catch(Common.oops(this, true))
4448
})
4549
}

plugins/plugin-core-support/src/test/core-support2/split-terminals.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ describe(`split terminals ${process.env.MOCHA_RUN_TARGET || ''}`, function(this:
8989
cd(dir1, 1)
9090
cwdIs(dir1, 1)
9191
count(1)
92-
splitTheTerminalViaCommand(2)
92+
splitTheTerminalViaCommand(2, false, true)
9393
count(2)
9494
focusOnSplit(1, 2)
9595
cwdIs(dir1, 2)

0 commit comments

Comments
 (0)