fix(terminal): display os specific copy/paste tooltip EE-1976 (#10835)

pull/11007/head
Dakota Walsh 2024-01-24 09:45:40 +13:00 committed by GitHub
parent fc7d9ca2cd
commit fe47318e26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 81 additions and 18 deletions

View File

@ -183,8 +183,11 @@ angular.module('portainer.docker').controller('ContainerConsoleController', [
socket.onopen = function () { socket.onopen = function () {
$scope.state = states.connected; $scope.state = states.connected;
term = new Terminal(); term = new Terminal();
socket.send('export LANG=C.UTF-8\n');
socket.send('export LC_ALL=C.UTF-8\n');
socket.send('clear\n');
term.on('data', function (data) { term.onData(function (data) {
socket.send(data); socket.send(data);
}); });
var terminal_container = document.getElementById('terminal-container'); var terminal_container = document.getElementById('terminal-container');

View File

@ -69,9 +69,11 @@
</div> </div>
</div> </div>
<div ng-if="state !== states.disconnected"> <div ng-if="state !== states.disconnected">
<label <label class="control-label text-left"
>Exec into container as <code>{{ ::formValues.user || 'default user' }}</code> using command >Exec &nbsp;into container as <code>{{ ::formValues.user || 'default user' }}</code> using command
<code>{{ formValues.isCustomCommand ? formValues.customCommand : formValues.command }}</code></label <code>{{ formValues.isCustomCommand ? formValues.customCommand : formValues.command }}</code>
<terminal-tooltip> </terminal-tooltip>
></label
> >
<button type="button" class="btn btn-primary" ng-click="disconnect()"> <button type="button" class="btn btn-primary" ng-click="disconnect()">
<span ng-show="state === states.connected">Disconnect</span> <span ng-show="state === states.connected">Disconnect</span>

View File

@ -30,6 +30,7 @@ import { SearchBar } from '@@/datatables/SearchBar';
import { FallbackImage } from '@@/FallbackImage'; import { FallbackImage } from '@@/FallbackImage';
import { BadgeIcon } from '@@/BadgeIcon'; import { BadgeIcon } from '@@/BadgeIcon';
import { TeamsSelector } from '@@/TeamsSelector'; import { TeamsSelector } from '@@/TeamsSelector';
import { TerminalTooltip } from '@@/TerminalTooltip';
import { PortainerSelect } from '@@/form-components/PortainerSelect'; import { PortainerSelect } from '@@/form-components/PortainerSelect';
import { Slider } from '@@/form-components/Slider'; import { Slider } from '@@/form-components/Slider';
import { TagButton } from '@@/TagButton'; import { TagButton } from '@@/TagButton';
@ -84,6 +85,7 @@ export const ngModule = angular
'portainerTooltip', 'portainerTooltip',
r2a(Tooltip, ['message', 'position', 'className', 'setHtmlMessage', 'size']) r2a(Tooltip, ['message', 'position', 'className', 'setHtmlMessage', 'size'])
) )
.component('terminalTooltip', r2a(TerminalTooltip, []))
.component('badge', r2a(Badge, ['type', 'className'])) .component('badge', r2a(Badge, ['type', 'className']))
.component('fileUploadField', fileUploadField) .component('fileUploadField', fileUploadField)
.component('porSwitchField', switchField) .component('porSwitchField', switchField)

View File

@ -0,0 +1,42 @@
import { BROWSER_OS_PLATFORM } from '@/react/constants';
import { Tooltip } from '@@/Tip/Tooltip';
const editorConfig = {
mac: {
tooltip: (
<>
<div>Within the console:</div>
<div>Cmd+C - Copy</div>
<div>Cmd+V - Paste</div>
<div>or right-click -&gt; Copy/Paste</div>
</>
),
},
lin: {
tooltip: (
<>
<div>Within the console:</div>
<div>Ctrl+Insert - Copy</div>
<div>Shift+Insert - Paste</div>
<div>or right-click -&gt; Copy/Paste</div>
</>
),
},
win: {
tooltip: (
<>
<div>Within the console:</div>
<div>Ctrl+Insert - Copy</div>
<div>Shift+Insert - Paste</div>
<div>or right-click -&gt; Copy/Paste</div>
</>
),
},
} as const;
export function TerminalTooltip() {
return <Tooltip message={editorConfig[BROWSER_OS_PLATFORM].tooltip} />;
}

View File

@ -0,0 +1 @@
export { TerminalTooltip } from './TerminalTooltip';

View File

@ -5,6 +5,7 @@ import { Terminal } from 'xterm';
import { baseHref } from '@/portainer/helpers/pathHelper'; import { baseHref } from '@/portainer/helpers/pathHelper';
import { notifyError } from '@/portainer/services/notifications'; import { notifyError } from '@/portainer/services/notifications';
import { TerminalTooltip } from '@/react/components/TerminalTooltip';
import { PageHeader } from '@@/PageHeader'; import { PageHeader } from '@@/PageHeader';
import { Widget, WidgetBody } from '@@/Widget'; import { Widget, WidgetBody } from '@@/Widget';
@ -74,6 +75,9 @@ export function ConsoleView() {
terminal?.setOption('cursorBlink', true); terminal?.setOption('cursorBlink', true);
terminal?.focus(); terminal?.focus();
setConnectionStatus('open'); setConnectionStatus('open');
socket.send('export LANG=C.UTF-8\n');
socket.send('export LC_ALL=C.UTF-8\n');
socket.send('clear\n');
} }
}; };
@ -93,7 +97,7 @@ export function ConsoleView() {
}, [disconnectConsole, setConnectionStatus, socket, terminal]); }, [disconnectConsole, setConnectionStatus, socket, terminal]);
useEffect(() => { useEffect(() => {
terminal?.on('data', (data) => { terminal?.onData((data) => {
socket?.send(data); socket?.send(data);
}); });
}, [terminal, socket]); }, [terminal, socket]);
@ -118,6 +122,7 @@ export function ConsoleView() {
className="col-sm-3 col-lg-2 control-label m-0 p-0 text-left" className="col-sm-3 col-lg-2 control-label m-0 p-0 text-left"
> >
Command Command
<TerminalTooltip />
</label> </label>
<div className="col-sm-8 input-group p-0"> <div className="col-sm-8 input-group p-0">
<span className="input-group-addon"> <span className="input-group-addon">

View File

@ -46,19 +46,27 @@ export function KubeCtlShell({ environmentId, onClose }: Props) {
onClose(); onClose();
}, [onClose, terminal, socket]); }, [onClose, terminal, socket]);
const openTerminal = useCallback(() => { const openTerminal = useCallback(
if (!terminalElem.current) { (socket: WebSocket | null) => {
return; if (!terminalElem.current) {
} return;
}
terminal.open(terminalElem.current); terminal.open(terminalElem.current);
terminal.setOption('cursorBlink', true); terminal.setOption('cursorBlink', true);
terminal.focus(); terminal.focus();
fit(terminal); fit(terminal);
terminal.writeln('#Run kubectl commands inside here'); if (socket) {
terminal.writeln('#e.g. kubectl get all'); socket.send('export LANG=C.UTF-8\n');
terminal.writeln(''); socket.send('export LC_ALL=C.UTF-8\n');
}, [terminal]); socket.send('clear\n');
}
terminal.writeln('#Run kubectl commands inside here');
terminal.writeln('#e.g. kubectl get all');
terminal.writeln('');
},
[terminal]
);
// refresh socket listeners on socket updates // refresh socket listeners on socket updates
useEffect(() => { useEffect(() => {
@ -66,7 +74,7 @@ export function KubeCtlShell({ environmentId, onClose }: Props) {
return () => {}; return () => {};
} }
function onOpen() { function onOpen() {
openTerminal(); openTerminal(socket);
} }
function onMessage(e: MessageEvent) { function onMessage(e: MessageEvent) {
terminal.write(e.data); terminal.write(e.data);