Files
portainer/app/docker/views/containers/console/exec.html
claude code agent 0bf4e71b79 fix(stacks): keep the stack breadcrumb trail on container attribute sub-tabs
When a container is opened from a stack, the detail tab kept the stack
trail (PR #7) but the attribute sub-tabs (Logs, Stats, Inspect, Console,
Attach) dropped it: those tabs were registered only under the global
docker.containers.container.* tree, so navigating to one left the stack
state (and its inherited params) behind, and each sub-view set a hardcoded
"Containers > ..." breadcrumb.

- Register stack-scoped child states docker.stacks.stack.container.{attach,
  exec,inspect,logs,stats} mirroring the global ones, so the inherited stack
  params survive and the trail can be kept.
- Centralize the breadcrumb logic in containerBreadcrumbs.ts (moved out of
  ItemView, which re-exports it) and add isStackContainerState +
  getContainerSubTabBreadcrumbs + buildStackContainerLinkParams.
- ActionLinksRow links sub-tabs into the stack tree (with stack+container
  params) when opened from a stack, else the global states unchanged.
- InspectView + the logs/stats/console controllers render the stack-aware
  trail; set up-front (no name) so it survives the load window and errors.

Covers regular/external/orphaned stacks and the non-stack fallback,
matching the existing ItemView breadcrumb behavior. New unit tests in
containerBreadcrumbs.test.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 02:25:04 +03:00

92 lines
4.6 KiB
HTML

<page-header title="'Container console'" breadcrumbs="breadcrumbs"> </page-header>
<div class="row" ng-init="initView()" ng-show="loaded">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="terminal" title-text="Execute"></rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<div ng-if="state === states.disconnected">
<!-- command-list -->
<div class="form-group">
<label for="command" class="col-lg-1 col-sm-2 control-label text-left">Command</label>
<div class="col-lg-11 col-sm-10">
<div class="input-group" ng-if="!formValues.isCustomCommand">
<span class="input-group-addon">
<pr-icon ng-if="imageOS == 'linux'" icon="'svg-linux'"></pr-icon>
<pr-icon ng-if="imageOS == 'windows'" icon="'layout-grid'"></pr-icon>
</span>
<select class="form-control" ng-model="formValues.command" id="command" data-cy="command-select">
<option value="ash" ng-if="imageOS == 'linux'">/bin/ash</option>
<option value="bash" ng-if="imageOS == 'linux'">/bin/bash</option>
<option value="dash" ng-if="imageOS == 'linux'">/bin/dash</option>
<option value="sh" ng-if="imageOS == 'linux'">/bin/sh</option>
<option value="powershell" ng-if="imageOS == 'windows'">powershell</option>
<option value="cmd.exe" ng-if="imageOS == 'windows'">cmd.exe</option>
<option ng-repeat="command in containerCommands" value="{{ command.command }}">{{ command.title }}: {{ command.command }}</option>
</select>
</div>
<input
class="form-control"
ng-if="formValues.isCustomCommand"
type="text"
name="custom-command"
ng-model="formValues.customCommand"
placeholder="e.g. ps aux"
data-cy="custom-command"
/>
</div>
</div>
<!-- !command-list -->
<div class="form-group col-lg-12">
<por-switch-field
label-class="'col-sm-1'"
checked="formValues.isCustomCommand"
label="'Use custom command'"
on-change="(handleIsCustomCommandChange)"
></por-switch-field>
</div>
<div class="form-group">
<label class="col-lg-1 col-sm-2 control-label text-left">
User
<portainer-tooltip message="'Format is one of: user, user:group, uid or uid:gid'"></portainer-tooltip>
</label>
<div class="col-lg-11 col-sm-10">
<input class="form-control" type="text" ng-model="formValues.user" placeholder="root" data-cy="container-exec-user" />
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary" ng-disabled="!container.State.Running" ng-click="connectExec()">
<span>Connect</span>
</button>
<span class="small text-danger vertical-center" ng-hide="container.State.Running">
<pr-icon icon="'alert-triangle'" mode="'danger'"></pr-icon>
The container is not running.
</span>
</div>
</div>
</div>
<div ng-if="state !== states.disconnected">
<label
>Exec into container as <code class="align-baseline !text-sm">{{ ::formValues.user || 'default user' }}</code> using command
<code class="align-baseline !text-sm">{{ formValues.isCustomCommand ? formValues.customCommand : formValues.command }}</code>
<terminal-tooltip class="align-sub"> </terminal-tooltip>
</label>
<button type="button" class="btn btn-primary" ng-click="disconnect()">
<span ng-show="state === states.connected">Disconnect</span>
<span ng-show="state === states.connecting">Connecting...</span>
</button>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<shell-terminal url="shellUrl" connect="shellConnect" on-state-change="(onShellStateChange)" on-resize="(onShellResize)" initial-commands="shellInitCommands"></shell-terminal>
</div>
</div>