diff --git a/fe-app-podkop/src/helpers/copyToClipboard.ts b/fe-app-podkop/src/helpers/copyToClipboard.ts new file mode 100644 index 0000000..b66cf50 --- /dev/null +++ b/fe-app-podkop/src/helpers/copyToClipboard.ts @@ -0,0 +1,12 @@ +export function copyToClipboard(text: string) { + const textarea = document.createElement('textarea'); + textarea.value = text; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + } catch (_err) { + console.error('copyToClipboard - e', _err); + } + document.body.removeChild(textarea); +} diff --git a/fe-app-podkop/src/helpers/downloadAsTxt.ts b/fe-app-podkop/src/helpers/downloadAsTxt.ts new file mode 100644 index 0000000..a0d9222 --- /dev/null +++ b/fe-app-podkop/src/helpers/downloadAsTxt.ts @@ -0,0 +1,15 @@ +export function downloadAsTxt(text: string, filename: string) { + const blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); + + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + + const safeName = filename.endsWith('.txt') ? filename : `${filename}.txt`; + link.download = safeName; + + document.body.appendChild(link); + link.click(); + + document.body.removeChild(link); + URL.revokeObjectURL(link.href); +} diff --git a/fe-app-podkop/src/partials/modal/renderModal.ts b/fe-app-podkop/src/partials/modal/renderModal.ts index 381e8e0..8c91742 100644 --- a/fe-app-podkop/src/partials/modal/renderModal.ts +++ b/fe-app-podkop/src/partials/modal/renderModal.ts @@ -1,6 +1,8 @@ import { renderButton } from '../button/renderButton'; +import { copyToClipboard } from '../../helpers/copyToClipboard'; +import { downloadAsTxt } from '../../helpers/downloadAsTxt'; -export function renderModal(text: string) { +export function renderModal(text: string, name: string) { return E( 'div', { class: 'pdk-partial-modal__body' }, @@ -8,10 +10,16 @@ export function renderModal(text: string) { E('pre', { class: 'pdk-partial-modal__content' }, E('code', {}, text)), E('div', { class: 'pdk-partial-modal__footer' }, [ + renderButton({ + classNames: ['cbi-button-apply'], + text: _('Download'), + onClick: () => downloadAsTxt(text, name), + }), renderButton({ classNames: ['cbi-button-apply'], text: _('Copy'), - onClick: () => {}, + onClick: () => + copyToClipboard(` \`\`\`${name} \n ${text} \n \`\`\``), }), renderButton({ classNames: ['cbi-button-remove'], diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts index 397a81f..70fbc6d 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts @@ -214,7 +214,10 @@ async function handleShowGlobalCheck() { const globalCheck = await PodkopShellMethods.globalCheck(); if (globalCheck.success) { - ui.showModal(_('Global check'), renderModal(globalCheck.data as string)); + ui.showModal( + _('Global check'), + renderModal(globalCheck.data as string, 'global_check'), + ); } } catch (e) { console.log('handleShowGlobalCheck - e', e); @@ -241,7 +244,10 @@ async function handleViewLogs() { const viewLogs = await PodkopShellMethods.checkLogs(); if (viewLogs.success) { - ui.showModal(_('View logs'), renderModal(viewLogs.data as string)); + ui.showModal( + _('View logs'), + renderModal(viewLogs.data as string, 'view_logs'), + ); } } catch (e) { console.log('handleViewLogs - e', e); @@ -270,7 +276,7 @@ async function handleShowSingBoxConfig() { if (showSingBoxConfig.success) { ui.showModal( _('Show sing-box config'), - renderModal(showSingBoxConfig.data as string), + renderModal(showSingBoxConfig.data as string, 'show_sing_box_config'), ); } } catch (e) { diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js index b9476a9..9370e99 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js @@ -2954,19 +2954,52 @@ function renderButton({ ); } +// src/helpers/copyToClipboard.ts +function copyToClipboard(text) { + const textarea = document.createElement("textarea"); + textarea.value = text; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand("copy"); + } catch (_err) { + console.error("copyToClipboard - e", _err); + } + document.body.removeChild(textarea); +} + +// src/helpers/downloadAsTxt.ts +function downloadAsTxt(text, filename) { + const blob = new Blob([text], { type: "text/plain;charset=utf-8" }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + const safeName = filename.endsWith(".txt") ? filename : `${filename}.txt`; + link.download = safeName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(link.href); +} + // src/partials/modal/renderModal.ts -function renderModal(text) { +function renderModal(text, name) { return E( "div", { class: "pdk-partial-modal__body" }, E("div", {}, [ E("pre", { class: "pdk-partial-modal__content" }, E("code", {}, text)), E("div", { class: "pdk-partial-modal__footer" }, [ + renderButton({ + classNames: ["cbi-button-apply"], + text: _("Download"), + onClick: () => downloadAsTxt(text, name) + }), renderButton({ classNames: ["cbi-button-apply"], text: _("Copy"), - onClick: () => { - } + onClick: () => copyToClipboard(` \`\`\`${name} + ${text} + \`\`\``) }), renderButton({ classNames: ["cbi-button-remove"], @@ -3464,7 +3497,10 @@ async function handleShowGlobalCheck() { try { const globalCheck = await PodkopShellMethods.globalCheck(); if (globalCheck.success) { - ui.showModal(_("Global check"), renderModal(globalCheck.data)); + ui.showModal( + _("Global check"), + renderModal(globalCheck.data, "global_check") + ); } } catch (e) { console.log("handleShowGlobalCheck - e", e); @@ -3488,7 +3524,10 @@ async function handleViewLogs() { try { const viewLogs = await PodkopShellMethods.checkLogs(); if (viewLogs.success) { - ui.showModal(_("View logs"), renderModal(viewLogs.data)); + ui.showModal( + _("View logs"), + renderModal(viewLogs.data, "view_logs") + ); } } catch (e) { console.log("handleViewLogs - e", e); @@ -3514,7 +3553,7 @@ async function handleShowSingBoxConfig() { if (showSingBoxConfig.success) { ui.showModal( _("Show sing-box config"), - renderModal(showSingBoxConfig.data) + renderModal(showSingBoxConfig.data, "show_sing_box_config") ); } } catch (e) {