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 {