diff --git a/usr/lib/dooke/_docker.py b/usr/lib/dooke/_docker.py
index 32ba4f6..c739fa9 100644
--- a/usr/lib/dooke/_docker.py
+++ b/usr/lib/dooke/_docker.py
@@ -51,4 +51,13 @@ class Docker:
"tag": it['Tag'],
'size': it['Size'],
'created': toISOTime(it['CreatedAt']),
- } for it in out]
\ No newline at end of file
+ } for it in out]
+
+
+ def rm(self, id = ''):
+ cmd = 'docker rm ' + id
+ out = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
+
+ def rmi(self, id = ''):
+ cmd = 'docker rmi ' + id
+ out = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
\ No newline at end of file
diff --git a/usr/lib/dooke/_window.py b/usr/lib/dooke/_window.py
index df3bf25..cad8156 100644
--- a/usr/lib/dooke/_window.py
+++ b/usr/lib/dooke/_window.py
@@ -7,7 +7,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class Window(Gtk.Window):
- def __init__(self, title = 'Untitled window', width = 960, height = 640):
+ def __init__(self, title = 'Untitled window', width = 896, height = 576):
Gtk.Window.__init__(self, title = title)
self.set_default_size(width, height)
self.resize(width, height)
diff --git a/usr/lib/dooke/dooke.py b/usr/lib/dooke/dooke.py
index c67e742..44a76e7 100755
--- a/usr/lib/dooke/dooke.py
+++ b/usr/lib/dooke/dooke.py
@@ -60,6 +60,12 @@ class Application(Gtk.Application):
case 'images':
output = client.images()
+
+ case 'rm':
+ client.rm(params.get('id'))
+
+ case 'rmi':
+ client.rmi(params.get('id'))
diff --git a/usr/lib/dooke/webapp/components/sidebar.js b/usr/lib/dooke/webapp/components/sidebar.js
index 016597b..5bf7c11 100644
--- a/usr/lib/dooke/webapp/components/sidebar.js
+++ b/usr/lib/dooke/webapp/components/sidebar.js
@@ -52,7 +52,7 @@ class Sidebar extends Component {
background: #fff;
}
.item.active {
- color: var(--color-orange-3);
+ color: var(--color-blue-3);
box-shadow: 0 0 6px rgba(0, 0, 0, 0.05);
}
`
diff --git a/usr/lib/dooke/webapp/lib/ui/modal/popconfirm.js b/usr/lib/dooke/webapp/lib/ui/modal/popconfirm.js
index 1794c55..3298d09 100644
--- a/usr/lib/dooke/webapp/lib/ui/modal/popconfirm.js
+++ b/usr/lib/dooke/webapp/lib/ui/modal/popconfirm.js
@@ -1,4 +1,4 @@
-import{css as p,html as c,Component as l,bind as a,unbind as d,styleMap as f,offset as h,outsideClick as m,clearOutsideClick as x}from"wkit";import"../base/button.js";const w="\u8BF7\u786E\u8BA4\u4F60\u7684\u64CD\u4F5C!";class u extends l{static props={title:"str!",confirmButtonType:"str!primary"};static styles=[p`:host{display:inline-flex}.container{position:relative}.tooltip{display:none;position:fixed;z-index:9;justify-content:center;align-items:center;max-width:260px;min-width:32px;padding:8px 12px 12px;border-radius:3px;font-size:var(--wc-popconfirm-font, 14px);background:var(--wc-popconfirm-background, #fff);color:var(--wc-popconfirm-color, var(--color-dark-1));box-shadow:0 0 3px var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.3));word-break:break-all;-webkit-user-select:none;user-select:none}.tooltip::after{position:absolute;display:block;width:8px;height:8px;border-radius:2px;background:var(--wc-popconfirm-background, #fff);content:"";transform:rotate(45deg);box-shadow:-1px -1px 0 var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-top]::after{right:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-bottom]::after{right:16px;top:-4px}.tooltip[placement=right-top]::after{left:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=right-bottom]::after{left:16px;top:-4px}`,p`.title{display:flex;align-items:center;gap:6px;padding:8px 0;--wc-icon-size: 16px}.title wc-icon{flex-shrink:0;color:var(--wc-popconfirm-icon-color, var(--color-red-1))}:host([hide-icon]) .title wc-icon{display:none}.actions{display:flex;justify-content:flex-end;gap:4px;margin-top:8px}.actions wc-button{min-width:40px;height:20px;font-size:12px}.actions wc-button:first-child{--wc-button-border-color: none}`];#t=!1;#i(s){let{left:t,top:i}=h(this),e="left",o={display:"block"},r=document.documentElement.scrollTop;if(!this.#t){if(t<260||t>260&&window.innerWidth-t>260)e="right",o.left=t+"px";else{let n=window.innerWidth-t+this.clientWidth;o.right=n+"px"}if(i<160)i+=8+this.clientHeight,e+="-bottom",o.top=i+"px";else{let n=window.innerHeight-i+8;e+="-top",o.bottom=n+"px",r>0&&(o.transform=`translateY(-${r}px)`)}this.#t=!0,this.$refs.tips.setAttribute("placement",e),this.$refs.tips.style.cssText=f(o),this.$refs.tips.$animate()}}#o(){this.#t&&(this.#t=!1,this.$refs.tips.$animate(!0))}#e(){this.$emit("confirm"),this.#o()}mounted(){this._outFn=m(this,s=>this.#o()),this._scrollFn=a(document,"scroll",s=>{if(this.#t){let t=document.documentElement.scrollTop;this.$refs.tips.style.transform=`translateY(-${t}px)`}})}unmounted(){d(document,"scroll",this._scrollFn),x(this._outFn)}render(){return c`
+import{css as p,html as c,Component as l,bind as a,unbind as d,styleMap as f,offset as h,outsideClick as m,clearOutsideClick as x}from"wkit";import"../base/button.js";const w="\u8BF7\u786E\u8BA4\u4F60\u7684\u64CD\u4F5C!";class u extends l{static props={title:"str!",confirmButtonType:"str!primary"};static styles=[p`:host{display:inline-flex}.container{position:relative}.tooltip{display:none;position:fixed;z-index:9;justify-content:center;align-items:center;max-width:260px;min-width:32px;padding:8px 12px 12px;border-radius:3px;font-size:var(--wc-popconfirm-font, 14px);background:var(--wc-popconfirm-background, #fff);color:var(--wc-popconfirm-color, var(--color-dark-1));box-shadow:0 0 3px var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.3));word-break:break-all;-webkit-user-select:none;user-select:none}.tooltip::after{position:absolute;display:block;width:8px;height:8px;border-radius:2px;background:var(--wc-popconfirm-background, #fff);content:"";transform:rotate(45deg);box-shadow:-1px -1px 0 var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-top]::after{right:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-bottom]::after{right:16px;top:-4px}.tooltip[placement=right-top]::after{left:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-popconfirm-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=right-bottom]::after{left:16px;top:-4px}`,p`.title{display:flex;align-items:center;gap:6px;padding:8px 0;--wc-icon-size: 16px}.title wc-icon{flex-shrink:0;color:var(--wc-popconfirm-icon-color, var(--color-red-1))}:host([hide-icon]) .title wc-icon{display:none}.actions{display:flex;justify-content:flex-end;gap:4px;margin-top:8px}.actions wc-button{min-width:40px;height:20px;font-size:12px}.actions wc-button:first-child{--wc-button-border-color: none}`];#t=!1;#i(s){let{left:t,top:e}=h(this),n="left",o={display:"block"},r=document.documentElement.scrollTop;if(!this.#t){if(t<260||t>260&&window.innerWidth-t>260)n="right",o.left=t+"px";else{let i=window.innerWidth-t-this.clientWidth-12;i<0&&(i=0),o.right=i+"px"}if(e<160)e+=8+this.clientHeight,n+="-bottom",o.top=e+"px";else{let i=window.innerHeight-e+8;n+="-top",o.bottom=i+"px",r>0&&(o.transform=`translateY(-${r}px)`)}this.#t=!0,this.$refs.tips.setAttribute("placement",n),this.$refs.tips.style.cssText=f(o),this.$refs.tips.$animate()}}#o(){this.#t&&(this.#t=!1,this.$refs.tips.$animate(!0))}#e(){this.$emit("confirm"),this.#o()}mounted(){this._outFn=m(this,s=>this.#o()),this._scrollFn=a(document,"scroll",s=>{if(this.#t){let t=document.documentElement.scrollTop;this.$refs.tips.style.transform=`translateY(-${t}px)`}})}unmounted(){d(document,"scroll",this._scrollFn),x(this._outFn)}render(){return c`
@@ -17,7 +17,6 @@ import{css as p,html as c,Component as l,bind as a,unbind as d,styleMap as f,off
>确定
-
`}}u.reg("popconfirm");
diff --git a/usr/lib/dooke/webapp/lib/ui/modal/tooltip.js b/usr/lib/dooke/webapp/lib/ui/modal/tooltip.js
index 27287fc..001617e 100644
--- a/usr/lib/dooke/webapp/lib/ui/modal/tooltip.js
+++ b/usr/lib/dooke/webapp/lib/ui/modal/tooltip.js
@@ -1,8 +1,8 @@
-import{css as a,html as n,Component as d,bind as r,styleMap as c,offset as f}from"wkit";class h extends d{static props={title:"str!"};static styles=[a`:host{display:inline-flex}.container{position:relative}.tooltip{display:none;position:fixed;z-index:9;justify-content:center;align-items:center;max-width:360px;min-width:32px;padding:6px 8px;border-radius:3px;font-size:var(--wc-tooltip-font, 14px);background:var(--wc-tooltip-background, #fff);color:var(--wc-tooltip-color, var(--color-dark-1));box-shadow:0 0 3px var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.3));word-break:break-all;-webkit-user-select:none;user-select:none}.tooltip::after{position:absolute;display:block;width:8px;height:8px;border-radius:2px;background:var(--wc-tooltip-background, #fff);content:"";transform:rotate(45deg);box-shadow:-1px -1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-top]::after{right:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-bottom]::after{right:16px;top:-4px}.tooltip[placement=right-top]::after{left:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=right-bottom]::after{left:16px;top:-4px}`];mounted(){r(this.$refs.wrap,"mouseenter",p=>{let{left:t,top:e}=f(this),i="left",o={display:"block"},l=document.documentElement.scrollTop;if(this.title.trim()!==""){if(t<360||t>360&&window.innerWidth-t>360)i="right",o.left=t+"px";else{let s=window.innerWidth-t-this.clientWidth;o.right=s+"px"}if(e<96)e+=8+this.clientHeight,i+="-bottom",o.top=e+"px";else{let s=window.innerHeight-e+8+l;i+="-top",o.bottom=s+"px"}this.$refs.tips.setAttribute("placement",i),this.$refs.tips.style.cssText=c(o),this.$refs.tips.$animate()}}),r(this.$refs.wrap,"mouseleave",p=>{this.$refs.tips.$animate(!0)})}render(){return n`
+import{css as a,html as n,Component as d,bind as s,styleMap as c,offset as f}from"wkit";class h extends d{static props={title:"str!"};static styles=[a`:host{display:inline-flex}.container{position:relative}.tooltip{display:none;position:fixed;z-index:9;justify-content:center;align-items:center;max-width:360px;min-width:32px;padding:6px 8px;border-radius:3px;font-size:var(--wc-tooltip-font, 14px);background:var(--wc-tooltip-background, #fff);color:var(--wc-tooltip-color, var(--color-dark-1));box-shadow:0 0 3px var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.3));word-break:break-all;-webkit-user-select:none;user-select:none}.tooltip::after{position:absolute;display:block;width:8px;height:8px;border-radius:2px;background:var(--wc-tooltip-background, #fff);content:"";transform:rotate(45deg);box-shadow:-1px -1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-top]::after{right:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=left-bottom]::after{right:16px;top:-4px}.tooltip[placement=right-top]::after{left:16px;bottom:-4px;box-shadow:1px 1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1))}.tooltip[placement=right-bottom]::after{left:16px;top:-4px}`];mounted(){s(this.$refs.wrap,"mouseenter",p=>{let{left:t,top:e}=f(this),i="left",o={display:"block"},l=document.documentElement.scrollTop;if(this.title.trim()!==""){if(t<360||t>360&&window.innerWidth-t>360)i="right",o.left=t+"px";else{let r=window.innerWidth-t-this.clientWidth;o.right=r+"px"}if(e<96)e+=8+this.clientHeight,i+="-bottom",o.top=e+"px";else{let r=window.innerHeight-e+8+l;i+="-top",o.bottom=r+"px"}this.$refs.tips.setAttribute("placement",i),this.$refs.tips.style.cssText=c(o),this.$refs.tips.$animate()}}),s(this.$refs.wrap,"mouseleave",p=>{this.$refs.tips.$animate(!0)})}render(){return n`
- ${this.title}
+ ${this.title}
`}}h.reg("tooltip");
diff --git a/usr/lib/dooke/webapp/utils/index.js b/usr/lib/dooke/webapp/utils/index.js
index 5f5c0be..9725cbf 100644
--- a/usr/lib/dooke/webapp/utils/index.js
+++ b/usr/lib/dooke/webapp/utils/index.js
@@ -15,3 +15,11 @@ export function getContainers(all = true) {
export function getImages() {
return native.handler('docker', { action: 'images' })
}
+
+export function removeContainer(id) {
+ return native.handler('docker', { action: 'rm', id })
+}
+
+export function removeImage(id) {
+ return native.handler('docker', { action: 'rmi', id })
+}
diff --git a/usr/lib/dooke/webapp/views/home.js b/usr/lib/dooke/webapp/views/home.js
index 0348b45..dbdaf07 100644
--- a/usr/lib/dooke/webapp/views/home.js
+++ b/usr/lib/dooke/webapp/views/home.js
@@ -5,7 +5,7 @@
*/
import { html, css, Component, classMap, nextTick, outsideClick } from 'wkit'
-import { noop, getContainers } from '../utils/index.js'
+import { getContainers, removeContainer } from '../utils/index.js'
class Home extends Component {
static props = {
@@ -48,15 +48,18 @@ class Home extends Component {
border-bottom: 1px solid var(--color-plain-3);
}
.main .list {
- overflow: hidden;
+ overflow: auto;
flex: 1;
display: flex;
flex-direction: column;
gap: 3px;
width: 100%;
+ height: 100%;
padding: 12px 0;
}
- .item {
+ .item,
+ .thead {
+ flex-shrink: 0;
display: flex;
align-items: center;
gap: 12px;
@@ -67,38 +70,62 @@ class Home extends Component {
border-radius: 24px;
--wc-icon-size: 16px;
+ }
+ .field {
+ overflow: hidden;
+ flex: 1;
- &:hover {
- background: var(--color-plain-1);
+ &.flex {
+ display: flex;
}
- .field {
+ &.flex-2 {
+ flex: 2;
+ }
+
+ &.name {
+ display: flex;
+ flex-direction: column;
+ }
+
+ &.center {
+ justify-content: center;
+ text-align: center;
+ }
+
+ &.gap-12 {
+ gap: 12px;
+ }
+
+ .text-ell {
overflow: hidden;
- flex: 1;
+ display: block;
+ width: 100%;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ color: var(--color-blue-1);
+ }
- &.flex-2 {
- flex: 2;
+ .action {
+ cursor: pointer;
+
+ &.red {
+ color: var(--color-red-1);
}
-
- &.name {
- display: flex;
- flex-direction: column;
- }
-
- &.center {
- text-align: center;
- }
-
- .text-ell {
- overflow: hidden;
- display: block;
- width: 100%;
- white-space: nowrap;
- text-overflow: ellipsis;
+ &.green {
color: var(--color-teal-1);
}
}
}
+
+ .thead {
+ color: var(--color-dark-2);
+ }
+ .item {
+ &:hover {
+ background: var(--color-plain-1);
+ }
+ }
`
]
@@ -107,7 +134,6 @@ class Home extends Component {
mounted() {
this.#all = +localStorage.getItem('container_mode') === 1
- console.log('<><><>', this.#all)
this.#fetch()
}
@@ -115,8 +141,17 @@ class Home extends Component {
let list = await getContainers(this.#all)
list.forEach(it => {
- it.port = it.port.replace('0.0.0.0:', '').replace(':::', '')
+ it.image = it.image.split(':')
+ it.cmd = it.cmd.replace(/^"|"$/, '')
+ it.port = it.port
+ .replace('0.0.0.0:', '')
+ .replace(':::', '')
+ .replace(/\/tcp/g, '')
+ it.state =
+ it.state === 'running' ? '🟢' : it.state === 'created' ? '⚪' : '🔴'
})
+
+ console.log(list)
this.containers = list
}
@@ -136,6 +171,12 @@ class Home extends Component {
this.$requestUpdate()
}
+ async #remove(item) {
+ console.log(item)
+ await removeContainer(item.id)
+ this.#fetch()
+ }
+
render() {
let list = this.containers
let txt = this.#input
@@ -158,6 +199,14 @@ class Home extends Component {
+ -
+ 容器
+ 所属镜像
+ 命令
+ 端口
+ 状态
+ 操作
+
${list.map(
it => html`
-
@@ -168,18 +217,30 @@ class Home extends Component {
${it.id}
-
- ${it.image}
+ ${it.image[0]}
+ ${it.image[1]}
+
+
-
-
-
+
+
+ ${it.state === '🟢' ? '⏹' : '⏸'}
+ this.#remove(it)}
+ >
+ 🗑
+
`
diff --git a/usr/lib/dooke/webapp/views/images.js b/usr/lib/dooke/webapp/views/images.js
index 4bb1b4d..f9d72ff 100644
--- a/usr/lib/dooke/webapp/views/images.js
+++ b/usr/lib/dooke/webapp/views/images.js
@@ -48,15 +48,18 @@ class Images extends Component {
border-bottom: 1px solid var(--color-plain-3);
}
.main .list {
- overflow: hidden;
+ overflow: auto;
flex: 1;
display: flex;
flex-direction: column;
gap: 3px;
width: 100%;
+ height: 100%;
padding: 12px 0;
}
- .item {
+ .item,
+ .thead {
+ flex-shrink: 0;
display: flex;
align-items: center;
gap: 12px;
@@ -67,36 +70,40 @@ class Images extends Component {
border-radius: 24px;
--wc-icon-size: 16px;
+ }
+ .field {
+ overflow: hidden;
+ flex: 1;
- &:hover {
- background: var(--color-plain-1);
+ &.flex-2 {
+ flex: 2;
}
- .field {
+ &.name {
+ display: flex;
+ flex-direction: column;
+ }
+
+ &.center {
+ text-align: center;
+ }
+
+ .text-ell {
overflow: hidden;
- flex: 1;
+ display: block;
+ width: 100%;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ color: var(--color-teal-1);
+ }
+ }
- &.flex-2 {
- flex: 2;
- }
-
- &.name {
- display: flex;
- flex-direction: column;
- }
-
- &.center {
- text-align: center;
- }
-
- .text-ell {
- overflow: hidden;
- display: block;
- width: 100%;
- white-space: nowrap;
- text-overflow: ellipsis;
- color: var(--color-teal-1);
- }
+ .thead {
+ color: var(--color-dark-2);
+ }
+ .item {
+ &:hover {
+ background: var(--color-plain-1);
}
}
`
@@ -138,6 +145,13 @@ class Images extends Component {
+ -
+ 镜像
+ 版本
+ 大小
+ 创建时间
+ 操作
+
${list.map(
it => html`
-