diff --git a/package.json b/package.json index fc4de384c..334cb9c6c 100644 --- a/package.json +++ b/package.json @@ -108,4 +108,4 @@ "yargs": "^17.7.2" }, "browserslist": "cover 100%,not android < 5" -} \ No newline at end of file +} diff --git a/src/pages/plugin/plugin.js b/src/pages/plugin/plugin.js index 82202cf52..d8a203460 100644 --- a/src/pages/plugin/plugin.js +++ b/src/pages/plugin/plugin.js @@ -79,6 +79,11 @@ export default async function PluginInclude( const description = await fsOperation( Url.join(PLUGIN_DIR, id, "readme.md"), ).readFile("utf8"); + const changelog = installedPlugin.changelog + ? await fsOperation( + Url.join(PLUGIN_DIR, id, installedPlugin.changelog), + ).readFile("utf8") + : ""; const iconUrl = await helpers.toInternalUri( Url.join(PLUGIN_DIR, id, "icon.png"), ); @@ -94,7 +99,11 @@ export default async function PluginInclude( author: author.name, author_github: author.github, source: installedPlugin.source, + license: installedPlugin.license, + keywords: installedPlugin.keywords, + contributors: installedPlugin.contributors, description, + changelog, }; isPaid = installedPlugin.price > 0; @@ -302,6 +311,19 @@ export default async function PluginInclude( }) .use(markdownItTaskLists) .render(plugin.description), + changelog: plugin.changelog + ? markdownIt({ html: true, xhtmlOut: true }) + .use(MarkdownItGitHubAlerts) + .use(anchor, { + slugify: (s) => + s + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-"), + }) + .use(markdownItTaskLists) + .render(plugin.changelog) + : null, purchased, installed, update, diff --git a/src/pages/plugin/plugin.scss b/src/pages/plugin/plugin.scss index af529bddc..3f142b90b 100644 --- a/src/pages/plugin/plugin.scss +++ b/src/pages/plugin/plugin.scss @@ -3,218 +3,278 @@ #plugin { overflow: auto; max-width: 800px; - margin: auto; - - .header { - display: flex; - flex-direction: column; - - .author { - display: flex; - padding: 10px 0; - gap: 2px; - - .verified { - height: 12px; - width: 12px; - background-size: contain; - background-position: center; - } + padding: 20px; + margin: 0 auto; + + .plugin-header { + display: grid; + grid-template-columns: auto 1fr auto; + gap: 20px; + align-items: start; + margin-bottom: 24px; + .plugin-icon { + width: 80px; + height: 80px; + border-radius: 16px; + background-position: center; + background-repeat: no-repeat; + background-size: contain; } - - .tag { - display: flex; - align-items: center; - justify-content: center; - min-height: 20px; - height: 20px; - width: fit-content; - padding: 0 10px; - border-radius: 10px; - background-color: rgb(51, 153, 255); - background-color: var(--button-background-color); - color: rgb(255, 255, 255); - color: var(--button-text-color); - margin: 10px 10px 10px 0; - - &:last-child { - margin-right: 0; + .plugin-info { + .plugin-name { + font-size: 24px; + margin-bottom: 8px; + } + .plugin-meta { + display: flex; + gap: 16px; + flex-wrap: wrap; + color: color-mix(in srgb, var(--primary-text-color) 60%, transparent); + font-size: 14px; + margin-bottom: 12px; + .author-name { + display: inline-flex; + align-items: center; + gap: 4px; + a { + text-decoration: none; + color: inherit; + } + } + .verified-tick { + color: #3b82f6; + font-size: 16px; + } } - } - - .more-info { - width: 100%; - font-size: 0.8rem; - display: flex; - overflow: auto; - .icon-info { + .metrics-row { display: flex; - flex-direction: column; - flex: 1; + gap: 16px; + color: color-mix(in srgb, var(--primary-text-color) 60%, transparent); + font-size: 14px; - .icon { - width: fit-content; - margin: 0 auto; - } + .metric { + display: flex; + align-items: center; + gap: 4px; + .metric-value { + color: var(--primary-text-color); + font-weight: 500; + } + .rating-value { + padding: 2px 8px; + border-radius: 12px; + font-weight: 600; + } - &::after { - content: attr(data-label); - font-size: smaller; - margin-top: 5px; - text-align: center; - } + .rating-high { + background: var(--link-text-color); + color: #0a3600; + } - > div { - .icon { - margin: 0; + .rating-medium { + background: #f0a500; + color: #3d2800; } - display: flex; - justify-content: center; + .rating-low { + background: var(--error-text-color); + color: #fff; + } } - - &:not(:last-child) { - border-right: solid 1px rgba($color: white, $alpha: 0.2); + } + .keywords { + display: flex; + gap: 6px; + flex-wrap: wrap; + margin-top: 16px; + position: relative; + + .keyword { + background: color-mix( + in srgb, + var(--link-text-color) 10%, + transparent + ); + color: var(--link-text-color); + padding: 6px 10px; + border-radius: 12px; + font-size: 13px; + transition: all 0.2s; + border: 1px solid + color-mix(in srgb, var(--link-text-color) 25%, transparent); } } } - - .info { + .action-buttons { display: flex; - padding: 0 20px 0 20px; + gap: 8px; + + .error { + display: flex; + color: rgb(255, 185, 92); + color: var(--error-text-color); + align-items: center; - .icon { + a { + color: inherit; + text-decoration: none; + } + } + + .btn { + padding: 8px 16px; + border-radius: 6px; + border: none; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; display: inline-flex; + align-items: center; + gap: 6px; + &:hover { + transform: translateY(-1px); + } + } + .btn-install { + background: var(--button-background-color); + color: white; } - .logo { - height: 100px; - width: 100px; - background-position: center; - background-repeat: no-repeat; - background-size: 80px; + .btn-update { + background: var(--button-background-color); + color: white; } - a { - max-width: fit-content; + .btn-uninstall { + background-color: var(--danger-color) !important; + color: white; + } + .btn-secondary { + background: var(--primary-color); + color: var(--primary-text-color); + shadow: 0 0 10px var(--box-shadow-color); } + } + .more-info-small { + text-align: center; + font-style: italic; + font-size: 0.8rem; + opacity: 0.8; + } + } - .desc { - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; + #plugin-tab { + .options { + display: flex; + gap: 0; + margin: 20px -20px; + padding: 0 20px; + border-bottom: 1px solid var(--border-color); - :not(.name) { - font-size: 0.8rem; - } + .tab { + color: color-mix(in srgb, var(--primary-text-color) 60%, transparent); + } - .name { - margin-bottom: 5px; - } + .tab.active { + color: var(--primary-text-color); } } - } - .more-info-small { - text-align: center; - font-style: italic; - font-size: 0.8rem; - opacity: 0.8; - } + .tab-content { + padding: 0 0 24px; + } - .button-container { - padding: 0 10px; - margin: 10px 0; - box-sizing: border-box; + .content-section { + display: none; + } + + .content-section.active { + display: block; + } - button { - margin: 0 10px; - border-radius: 4px; - max-width: 300px; + .contributor { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + text-decoration: none; - @media (max-width: 800px) { - &:only-child { - margin: 0 auto; - } + &:last-child { + border-bottom: none; } - &[data-type="uninstall"] { - background-color: rgb(160, 51, 0) !important; - background-color: var(--danger-color) !important; - color: rgb(255, 255, 255) !important; - color: var(--danger-text-color) !important; + img { + width: 32px; + height: 32px; + border-radius: 50%; } - } - .error { - display: flex; - color: rgb(255, 185, 92); - color: var(--error-text-color); - align-items: center; + .contributor-info { + flex-grow: 1; - a { - color: inherit; - text-decoration: none; + .contributor-name { + font-weight: 500; + margin-bottom: 2px; + color: var(--primary-text-color); + } + + .contributor-role { + font-size: 13px; + color: color-mix(in srgb, var(--primary-text-color) 60%, transparent); + } } } - } - - .body { - text-align: justify; - padding: 10px; - box-sizing: border-box; - - pre, - code, - h1, - h2, - h3, - h4, - h5, - h6 { - text-align: left; - &[align="center"] { + #changelog { + .no-changelog { text-align: center; + padding: 2rem; + color: color-mix(in srgb, var(--primary-text-color) 60%, transparent); + i { + font-size: 3rem; + margin-bottom: 1rem; + opacity: 0.7; + display: block; + } + p { + margin: 0.5rem 0; + } } } + } - // class="language-xxx" - [class*="language-"] { - word-wrap: normal; - white-space: pre; - font-size: 0.8rem; - padding: 5px; - margin: 5px 0; - background-color: rgb(153, 153, 255); - background-color: var(--primary-color); - color: rgb(255, 255, 255); - color: var(--primary-text-color); - border-left: solid 5px rgb(51, 153, 255); - border-left: solid 5px var(--active-color); - overflow: auto; - width: calc(100% - 10px); - display: block; - user-select: text; + @media (max-width: 768px) { + .plugin-header { + grid-template-columns: 1fr; + justify-items: center; + text-align: center; } - } - .version { - display: flex; - align-items: center; - } + .plugin-meta, + .metrics-row, + .keywords { + justify-content: center; + } + + .action-buttons { + flex-direction: column; + width: 100%; + } - h1, - h2, - h3, - h4, - h5, - h6 { - margin: 10px 0; + .btn { + width: 100%; + justify-content: center; + } + + .tabs { + overflow-x: auto; + margin: 24px -24px; + padding: 0 24px; + } } } - .reviews-container { position: fixed; display: flex; diff --git a/src/pages/plugin/plugin.view.js b/src/pages/plugin/plugin.view.js index 1cfb1f8f3..ff4d59de8 100644 --- a/src/pages/plugin/plugin.view.js +++ b/src/pages/plugin/plugin.view.js @@ -1,3 +1,4 @@ +import TabView from "components/tabView"; import toast from "components/toast"; import alert from "dialogs/alert"; import fsOperation from "fileSystem"; @@ -5,7 +6,7 @@ import Ref from "html-tag-js/ref"; import actionStack from "lib/actionStack"; import constants from "lib/constants"; import Url from "utils/Url"; - +import helpers from "utils/helpers"; export default (props) => { const { id, @@ -14,6 +15,10 @@ export default (props) => { icon, author, downloads, + license, + keywords, + contributors, + changelog, votes_up: votesUp, votes_down: votesDown, author_verified: authorVerified, @@ -22,11 +27,6 @@ export default (props) => { } = props; let rating = "unrated"; - let moreInfoStyle = {}; - - if (votesUp === undefined) { - moreInfoStyle.display = "none"; - } if (votesUp || votesDown) { rating = `${Math.round((votesUp / (votesUp + votesDown)) * 100)}%`; @@ -34,62 +34,178 @@ export default (props) => { return (
+ No changelog is available for this plugin yet. +
++ Check back later for updates! +
+