思源笔记折腾记录 -html 块 - 主题 css 颜色选择器

本贴最后更新于 724 天前,其中的信息可能已经事过景迁

首先是引入依赖,这里我们需要一个颜色选择器来修改主题样式,就用跟思源本身一样的 pickr 好了:

import Pickr from "@simonwep/pickr/dist/pickr.es5.min"; let pickClassic = ` .pickr{position:relative;overflow:visible;transform:translateY(0)}.pickr *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr .pcr-button{position:relative;height:2em;width:2em;padding:0.5em;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;border-radius:.15em;background:url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" stroke="%2342445A" stroke-width="5px" stroke-linecap="round"><path d="M45,45L5,5"></path><path d="M45,5L5,45"></path></svg>') no-repeat center;background-size:0;transition:all 0.3s}.pickr .pcr-button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');background-size:.5em;border-radius:.15em;z-index:-1}.pickr .pcr-button::before{z-index:initial}.pickr .pcr-button::after{position:absolute;content:'';top:0;left:0;height:100%;width:100%;transition:background 0.3s;background:var(--pcr-color);border-radius:.15em}.pickr .pcr-button.clear{background-size:70%}.pickr .pcr-button.clear::before{opacity:0}.pickr .pcr-button.clear:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-button.disabled{cursor:not-allowed}.pickr *,.pcr-app *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr input:focus,.pickr input.pcr-active,.pickr button:focus,.pickr button.pcr-active,.pcr-app input:focus,.pcr-app input.pcr-active,.pcr-app button:focus,.pcr-app button.pcr-active{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-palette,.pickr .pcr-slider,.pcr-app .pcr-palette,.pcr-app .pcr-slider{transition:box-shadow 0.3s}.pickr .pcr-palette:focus,.pickr .pcr-slider:focus,.pcr-app .pcr-palette:focus,.pcr-app .pcr-slider:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(0,0,0,0.25)}.pcr-app{position:fixed;display:flex;flex-direction:column;z-index:10000;border-radius:0.1em;background:#fff;opacity:0;visibility:hidden;transition:opacity 0.3s, visibility 0s 0.3s;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;box-shadow:0 0.15em 1.5em 0 rgba(0,0,0,0.1),0 0 1em 0 rgba(0,0,0,0.03);left:0;top:0}.pcr-app.visible{transition:opacity 0.3s;visibility:visible;opacity:1}.pcr-app .pcr-swatches{display:flex;flex-wrap:wrap;margin-top:0.75em}.pcr-app .pcr-swatches.pcr-last{margin:0}@supports (display: grid){.pcr-app .pcr-swatches{display:grid;align-items:center;grid-template-columns:repeat(auto-fit, 1.75em)}}.pcr-app .pcr-swatches>button{font-size:1em;position:relative;width:calc(1.75em - 5px);height:calc(1.75em - 5px);border-radius:0.15em;cursor:pointer;margin:2.5px;flex-shrink:0;justify-self:center;transition:all 0.15s;overflow:hidden;background:transparent;z-index:1}.pcr-app .pcr-swatches>button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');background-size:6px;border-radius:.15em;z-index:-1}.pcr-app .pcr-swatches>button::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:var(--pcr-color);border:1px solid rgba(0,0,0,0.05);border-radius:0.15em;box-sizing:border-box}.pcr-app .pcr-swatches>button:hover{filter:brightness(1.05)}.pcr-app .pcr-swatches>button:not(.pcr-active){box-shadow:none}.pcr-app .pcr-interaction{display:flex;flex-wrap:wrap;align-items:center;margin:0 -0.2em 0 -0.2em}.pcr-app .pcr-interaction>*{margin:0 0.2em}.pcr-app .pcr-interaction input{letter-spacing:0.07em;font-size:0.75em;text-align:center;cursor:pointer;color:#75797e;background:#f1f3f4;border-radius:.15em;transition:all 0.15s;padding:0.45em 0.5em;margin-top:0.75em}.pcr-app .pcr-interaction input:hover{filter:brightness(0.975)}.pcr-app .pcr-interaction input:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(66,133,244,0.75)}.pcr-app .pcr-interaction .pcr-result{color:#75797e;text-align:left;flex:1 1 8em;min-width:8em;transition:all 0.2s;border-radius:.15em;background:#f1f3f4;cursor:text}.pcr-app .pcr-interaction .pcr-result::-moz-selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-result::selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-type.active{color:#fff;background:#4285f4}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff;width:auto}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff}.pcr-app .pcr-interaction .pcr-save:hover,.pcr-app .pcr-interaction .pcr-cancel:hover,.pcr-app .pcr-interaction .pcr-clear:hover{filter:brightness(0.925)}.pcr-app .pcr-interaction .pcr-save{background:#4285f4}.pcr-app .pcr-interaction .pcr-clear,.pcr-app .pcr-interaction .pcr-cancel{background:#f44250}.pcr-app .pcr-interaction .pcr-clear:focus,.pcr-app .pcr-interaction .pcr-cancel:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(244,66,80,0.75)}.pcr-app .pcr-selection .pcr-picker{position:absolute;height:18px;width:18px;border:2px solid #fff;border-radius:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pcr-app .pcr-selection .pcr-color-palette,.pcr-app .pcr-selection .pcr-color-chooser,.pcr-app .pcr-selection .pcr-color-opacity{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;flex-direction:column;cursor:grab;cursor:-webkit-grab}.pcr-app .pcr-selection .pcr-color-palette:active,.pcr-app .pcr-selection .pcr-color-chooser:active,.pcr-app .pcr-selection .pcr-color-opacity:active{cursor:grabbing;cursor:-webkit-grabbing}.pcr-app[data-theme='classic']{width:28.5em;max-width:95vw;padding:0.8em}.pcr-app[data-theme='classic'] .pcr-selection{display:flex;justify-content:space-between;flex-grow:1}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-preview{position:relative;z-index:1;width:2em;display:flex;flex-direction:column;justify-content:space-between;margin-right:0.75em}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-preview::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-preview .pcr-last-color{cursor:pointer;border-radius:0.15em 0.15em 0 0;z-index:2}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-preview .pcr-current-color{border-radius:0 0 0.15em 0.15em}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-preview .pcr-last-color,.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-preview .pcr-current-color{background:var(--pcr-color);width:100%;height:50%}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-palette{width:100%;height:8em;z-index:1}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-palette .pcr-palette{flex-grow:1;border-radius:.15em}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-palette .pcr-palette::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-chooser,.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-opacity{margin-left:0.75em}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-chooser .pcr-picker,.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-opacity .pcr-picker{left:50%;transform:translateX(-50%)}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-chooser .pcr-slider,.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-opacity .pcr-slider{width:8px;flex-grow:1;border-radius:50em}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-chooser .pcr-slider{background:linear-gradient(to bottom, red, #ff0, lime, cyan, blue, #f0f, red)}.pcr-app[data-theme='classic'] .pcr-selection .pcr-color-opacity .pcr-slider{background:linear-gradient(to bottom, transparent, black),url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');background-size:100%, 50%} `;

然后再弄一个注册表,保证各个地方的颜色选择器获取到的颜色一致

import { 注册表 } from "siyuan-noob/commonStruct";

一开始的时候,我们就把主题目录下的 custom.css 重新插入回来(因为它被干掉了嘛)

let currentThemeElement = document.querySelector("#themeCustomStyle") || document.querySelector("#themeStyle") || document.querySelector("#themeDefaultStyle"); let currentThemeURL = currentThemeElement.getAttribute("href").split("?")[0]; if (!document.querySelector("#themeCustomStyle")) { let customThemeStyle = document.createElement("link"); customThemeStyle.setAttribute("id", "themeCustomStyle"); customThemeStyle.setAttribute("type", "text/css"); customThemeStyle.setAttribute("rel", "stylesheet"); customThemeStyle.setAttribute( "href", currentThemeURL.replace("theme.css", "custom.css") ); document.head.appendChild(customThemeStyle); }

然后还需要一个函数,用来从样式表元素(link 或者 style)来获取样式,就像这样

function getColor(element, name) { console.log(element.sheet) let rootRule for (let i = 0; i < element.sheet.cssRules.length; i++) { if (element.sheet.cssRules[i].selectorText === ":root") { rootRule = element.sheet.cssRules[i]; break; } } let namedColor; namedColor = rootRule.style.getPropertyValue(name); namedColor = namedColor ? namedColor.trim() : null; return namedColor }

再搞一个函数,用来临时设置样式

async function setstyle(items, save) { let tempstring = ""; for (let item of items) { item.value !== undefined ? (tempstring = tempstring + item.id + ":" + item.value + ";\n") : null; } tempstring = "{" + tempstring + "}"; let css = ":root" + tempstring; let styleSetterTemp = document.getElementById("styleSetterTemp"); if (!styleSetterTemp) { styleSetterTemp = document.createElement("style"); styleSetterTemp.innerText = css; styleSetterTemp.setAttribute("id", "styleSetterTemp"); document.head.appendChild(styleSetterTemp); } else { styleSetterTemp.innerText = css; document.head.appendChild(styleSetterTemp); } save ? saveStyle(css) : null; }

当确定下来之后,把它保存到 custom.css

async function saveStyle(css) { let blob = new Blob([css], { type: "text/css", }); let file = new File([blob], "custom.css", { lastModified: Date.now(), }); let data = new FormData(); data.append( "path", "/conf/" + currentThemeURL.replace("theme.css", "custom.css") ); data.append("file", file); data.append("isDir", false); data.append("modTime", Date.now()); await fetch("/api/file/putFile", { method: "POST", body: data, }); if (!document.querySelector("#themeCustomStyle")) { let customThemeStyle = document.createElement("link"); customThemeStyle.setAttribute("id", "themeCustomStyle"); customThemeStyle.setAttribute("type", "text/css"); customThemeStyle.setAttribute("rel", "stylesheet"); customThemeStyle.setAttribute( "href", currentThemeURL.replace("theme.css", "custom.css") ); document.head.appendChild(customThemeStyle); } else { let customThemeStyle = document.querySelector("#themeCustomStyle"); customThemeStyle.remove(); document.head.appendChild(customThemeStyle); } document.querySelectorAll("#styleSetterTemp").forEach((el) => { el.remove(); }); }

啊,终于可以开始写我们的 ThemeColorPicker 了

class ThemeColorPicker extends HTMLElement { constructor() { super(); this.css变量注册表 = new 注册表("css变量"); } getDefault() { try { let currentThemeElement = document.querySelector("#themeCustomStyle") || document.querySelector("#themeStyle") || document.querySelector("#themeDefaultStyle"); this.defaultColor = getColor(currentThemeElement, this.name); } catch (e) {} if (!this.defaultColor) { this.defaultColor = getColor( document.querySelector("#themeStyle") || document.querySelector("#themeDefaultStyle"), this.name ); } if (!this.defaultColor) { this.defaultColor = getColor( document.querySelector("#themeDefaultStyle"), this.name ); } return this.defaultColor; } connectedCallback() { document.getElementById("#pickrcss") ? null : document.head.insertAdjacentHTML( "beforeend", ` <style id='pickrcss'> ${pickClassic} </style>` ); this.attachShadow({ mode: "open" }); this.shadowRoot.innerHTML = ` <style> ${pickClassic} </style> <link href="/stage/build/desktop/base.56dfeeea3d0c5f4a8fef.css" rel="stylesheet"> <label class="b3-label fn__flex config__item"> <div class="fn__flex-1 fn__flex-center"> ${this.name} <div class="b3-label__text">${this.getAttribute("memo")}</div> </div> <span class="fn__space"></span> <span class="pickr"></span> </label> `; if (this.getAttribute("simple")) { this.shadowRoot.innerHTML = ` <style> ${pickClassic} </style> <link href="/stage/build/desktop/base.56dfeeea3d0c5f4a8fef.css" rel="stylesheet"> <span class="pickr"></span> `; } this.pickr = Pickr.create({ container: "body", el: this.shadowRoot.querySelector(".pickr"), default: this.getDefault(), theme: "classic", // or 'monolith', or 'nano' comparison: false, components: { preview: true, opacity: true, hue: true, interaction: { hex: true, rgba: true, hsla: true, hsva: true, cmyk: true, input: true, save: true, clear: true, }, }, }); this.pickr.on("change", (color) => { let colorValue = color ? color.toRGBA() : ""; this.css变量注册表.注册({ id: this.name, value: colorValue, }); setstyle(this.css变量注册表.list(), false); }); this.pickr.on("save", (color) => { let colorValue = color ? color.toRGBA() : ""; this.css变量注册表.注册({ id: this.name, value: colorValue, }); setstyle(this.css变量注册表.list(), true); }); this.pickr.on("clear", (color) => { this.pickr.setColor(this.getDefault()); this.css变量注册表.注销({ id: this.name, value: undefined, }); setstyle(this.css变量注册表.list(), true); }); } static get observedAttributes() { return ["name"]; } attributeChangedCallback(name, oldValue, newValue) { if (name === "name") { this.name = newValue; } } } customElements.define("theme-color-picker", ThemeColorPicker);

使用方法:

在主题中使用的时候,需要先运行这个代码片段,然后把运行产物(在 /snippets/jsInNote​里面)引入.

直接使用这个代码片段的话,在 html 块里面插入就行了

<theme-color-picker name="--b3-theme-background" memo="背景色" ></theme-color-picker>

theme-color-picker​元素的 name​属性就是要调节的 css 变量,memo​是显示出来的提示.

当然你也可以整点别的,simple 属性为 true​,这样可以自己写其他的部分,最后的效果反正就是这样:

image​​image

思源笔记折腾记录 - 运行你的笔记 - 链滴 (ld246.com)里面的方法来弄,运行到代码片段之后就可以用了

搞完收工,自己写主题配色器去吧~~

如果你想要了解更多原理的话,就自己去搜一下 stylesheet 对象吧,这个可以获取各种样式元素的规则,我就不罗嗦了.


水完收工,如果这玩意对你有用可以去爱发电给我买杯咖啡哒

leolee9086 正在创作一些简单的技术教程和小工具,以及设计方面内容 | 爱发电 (afdian.net)

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    25110 引用 • 103521 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Office

    Office 现已更名为 Microsoft 365. Microsoft 365 将高级 Office 应用(如 Word、Excel 和 PowerPoint)与 1 TB 的 OneDrive 云存储空间、高级安全性等结合在一起,可帮助你在任何设备上完成操作。

    5 引用 • 34 回帖 • 1 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    181 引用 • 400 回帖
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    199 引用 • 542 回帖 • 1 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 383 关注
  • 安全

    安全永远都不是一个小问题。

    203 引用 • 818 回帖
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    184 引用 • 1015 回帖
  • Hadoop

    Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

    89 引用 • 122 回帖 • 618 关注
  • 酷鸟浏览器

    安全 · 稳定 · 快速
    为跨境从业人员提供专业的跨境浏览器

    3 引用 • 59 回帖 • 46 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    588 引用 • 3538 回帖
  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    91 引用 • 384 回帖
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    83 引用 • 37 回帖
  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    34 引用 • 467 回帖 • 761 关注
  • 倾城之链
    23 引用 • 66 回帖 • 162 关注
  • danl
    165 关注
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 388 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    22 引用 • 148 回帖 • 12 关注
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    367 引用 • 1842 回帖
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 209 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 248 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1742 回帖
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    289 引用 • 4492 回帖 • 653 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    36 引用 • 155 回帖 • 1 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    298 引用 • 762 回帖
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    210 引用 • 2040 回帖
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 4 关注