├── .gitignore
├── .vscode
└── launch.json
├── .vscodeignore
├── README.md
├── changelog.md
├── demo
├── 1.png
├── 2.png
├── 3.png
└── usage.gif
├── icon.png
├── jsconfig.json
├── package.json
├── src
└── extension.js
├── webview
├── dom2image.js
├── index.html
├── index.js
└── vivus.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
3 | *.vsix
4 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch Polacode",
6 | "type": "extensionHost",
7 | "request": "launch",
8 | "runtimeExecutable": "${execPath}",
9 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"],
10 | "stopOnEntry": false
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | demo
3 | *.vsix
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
Polacode — Polaroid for your code 📸
3 |
4 |
5 |
6 | 
7 |
8 | ## Why?
9 |
10 | You have spent countless hours finding the perfect [JavaScript grammar](https://marketplace.visualstudio.com/search?term=javascript%20grammar&target=VSCode&category=All%20categories&sortBy=Relevance), matching it with a [sleek-looking VS Code theme](https://marketplace.visualstudio.com/search?target=VSCode&category=Themes&sortBy=Downloads), trying out all the [best programming fonts](https://www.slant.co/topics/67/~best-programming-fonts).
11 |
12 | You take three days porting over [your theme](https://github.com/wesbos/cobalt2-vscode) before starting to use VS Code.
13 | You shell out $200 for [italic cursive html attributes](https://www.typography.com/blog/introducing-operator).
14 |
15 | The code has to look right.
16 |
17 | ## Tips
18 |
19 | - Resize the snippet / container by dragging the lowerright corner
20 | - Use `polacode.target`, `polacode.shadow`, `polacode.transparentBackground` and `polacode.backgroundColor` to control image appearance
21 |
22 | ## Demo
23 |
24 | [Nord](https://github.com/arcticicestudio/nord-visual-studio-code) + [Input Mono](http://input.fontbureau.com)
25 |
26 | 
27 |
28 | [Monokai Pro](https://marketplace.visualstudio.com/items?itemName=monokai.theme-monokai-pro-vscode) + [Operator Mono](https://www.typography.com/blog/introducing-operator)
29 |
30 | 
31 |
32 | [Material Theme Palenight](https://marketplace.visualstudio.com/items?itemName=Equinusocio.vsc-material-theme) + [Fira Code](https://github.com/tonsky/FiraCode)
33 |
34 | 
35 |
36 | ## Credit
37 |
38 | Thanks to [@tsayen](https://github.com/tsayen) for making [dom-to-image](https://github.com/tsayen/dom-to-image), which Polacode is using for generating the images.
39 |
40 | Thanks to [Dawn Labs](https://dawnlabs.io) for making [Carbon](https://carbon.now.sh) that inspired Polacode.
41 |
42 | Many color are taken from the elegant [Nord](https://github.com/arcticicestudio/nord) theme by [@arcticicestudio](https://github.com/arcticicestudio).
43 |
44 | Download button animation is made with [Vivus](https://github.com/maxwellito/vivus).
45 |
46 | ## Contribution
47 |
48 | Contribution is not very welcome.
49 | Please open an issue first so I can stop you from complicating the UX.
50 |
51 | ## License
52 |
53 | MIT
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ### 0.3.4 | 2019-09-27
4 |
5 | - Fix Polacode not loading background color correctly. [#125](https://github.com/octref/polacode/issues/125).
6 |
7 | ### 0.3.3 | 2019-09-24
8 |
9 | - 🙌 Fix Polacode not loading `font-family` correctly. Thanks to contribution from [@kufii](https://github.com/kufii). [#121](https://github.com/octref/polacode/pull/121).
10 |
11 | ### 0.3.2 | 2019-05-10
12 |
13 | - Vertically center code in the snippet. #108.
14 |
15 | ### 0.3.1 | 2019-05-07
16 |
17 | - Dispose selection sync after closing Polacode window so selection no longer jams clipboard. #107.
18 |
19 | ### 0.3.0 | 2019-05-07
20 |
21 | - Major rewrite. See features in [#105](https://github.com/octref/polacode/pull/105).
22 | - Long token wrapping. Thanks to [OhYee](https://github.com/OhYee). [#104](https://github.com/octref/polacode/pull/104).
23 | - Improved vertical alignment of snippet. Thanks to [vxna](https://github.com/vxna). [#106](https://github.com/octref/polacode/pull/106).
24 |
25 | ### 0.2.2 | 2018-02-19
26 |
27 | - Remove "Pasted content is invalid" check. Will do proper HTML validation in [#30](https://github.com/octref/polacode/issues/30).
28 |
29 | ### 0.2.1 | 2018-02-19
30 |
31 | - Fix an issue where Polacode incorrectly reports "Pasted content is invalid".
32 |
33 | ### 0.2.0 | 2018-02-16
34 |
35 | - Add warning when pasted content is not valid HTML copy-pasted from VS Code.
36 | - Remove backdrop for snippet with light background.
37 | - Find the line with smallest indentation and detract that indentation from all lines.
38 | - Add `polacode.shoot` command that you can bind command to.
39 | - Remember last used image save path.
40 | - Wrap when code is longer than Polacode preview.
41 | - Initialize with correct fontFamily.
42 |
43 | ### 0.1.2 | 2018-02-16
44 |
45 | - Update readme with some tip and explanation.
46 | - Make default filename `code.png`.
47 |
48 | ### 0.1.1 | 2018-02-15
49 |
50 | - Fix a Windows path issue.
51 | - Use correct background for snippet when pasting.
52 |
53 | ### 0.1.0 | 2018-02-15
54 |
55 | - Initial release
--------------------------------------------------------------------------------
/demo/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/1.png
--------------------------------------------------------------------------------
/demo/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/2.png
--------------------------------------------------------------------------------
/demo/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/3.png
--------------------------------------------------------------------------------
/demo/usage.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/usage.gif
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/icon.png
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2015",
4 | "module": "commonjs",
5 | "moduleResolution": "node"
6 | }
7 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "polacode",
3 | "displayName": "Polacode",
4 | "description": "📸 Polaroid for your code",
5 | "version": "0.3.4",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/octref/polacode.git"
9 | },
10 | "publisher": "pnp",
11 | "keywords": [
12 | "polacode",
13 | "polaroid",
14 | "screenshot",
15 | "snippet",
16 | "share"
17 | ],
18 | "galleryBanner": {
19 | "color": "#fbfbfb",
20 | "theme": "light"
21 | },
22 | "icon": "icon.png",
23 | "categories": [
24 | "Other"
25 | ],
26 | "engines": {
27 | "vscode": "^1.32.0"
28 | },
29 | "activationEvents": [
30 | "onCommand:polacode.activate",
31 | "onWebviewPanel:polacode"
32 | ],
33 | "main": "./src/extension",
34 | "contributes": {
35 | "commands": [
36 | {
37 | "command": "polacode.activate",
38 | "title": "Polacode 📸"
39 | }
40 | ],
41 | "configuration": {
42 | "title": "Polacode",
43 | "properties": {
44 | "polacode.shadow": {
45 | "type": "string",
46 | "description": "Shadow of the snippet node. Use any value for CSS `box-shadow`",
47 | "default": "rgba(0, 0, 0, 0.55) 0px 20px 68px"
48 | },
49 | "polacode.transparentBackground": {
50 | "type": "boolean",
51 | "description": "Transparent background for containers",
52 | "default": false
53 | },
54 | "polacode.backgroundColor": {
55 | "type": "string",
56 | "description": "Background color of snippet container. Use any value for CSS `background-color`",
57 | "format": "color-hex",
58 | "default": "#f2f2f2"
59 | },
60 | "polacode.target": {
61 | "type": "string",
62 | "description": "Shoot with or without container",
63 | "default": "container",
64 | "enum": [
65 | "container",
66 | "snippet"
67 | ],
68 | "enumDescriptions": [
69 | "Shoot with the container.",
70 | "Shoot with the snippet alone. If you want transparent padding, use `container` with `\"polacode.transparentBackground\": true`"
71 | ]
72 | }
73 | }
74 | }
75 | },
76 | "devDependencies": {
77 | "@types/node": "^11.12.0",
78 | "@types/vscode": "^1.32.0"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/extension.js:
--------------------------------------------------------------------------------
1 | const vscode = require('vscode')
2 | const fs = require('fs')
3 | const path = require('path')
4 | const { homedir } = require('os')
5 |
6 | const writeSerializedBlobToFile = (serializeBlob, fileName) => {
7 | const bytes = new Uint8Array(serializeBlob.split(','))
8 | fs.writeFileSync(fileName, Buffer.from(bytes))
9 | }
10 |
11 | const P_TITLE = 'Polacode 📸'
12 |
13 | /**
14 | * @param {vscode.ExtensionContext} context
15 | */
16 | function activate(context) {
17 | const htmlPath = path.resolve(context.extensionPath, 'webview/index.html')
18 |
19 | let lastUsedImageUri = vscode.Uri.file(path.resolve(homedir(), 'Desktop/code.png'))
20 | let panel
21 |
22 | vscode.window.registerWebviewPanelSerializer('polacode', {
23 | async deserializeWebviewPanel(_panel, state) {
24 | panel = _panel
25 | panel.webview.html = getHtmlContent(htmlPath)
26 | panel.webview.postMessage({
27 | type: 'restore',
28 | innerHTML: state.innerHTML,
29 | bgColor: context.globalState.get('polacode.bgColor', '#2e3440')
30 | })
31 | const selectionListener = setupSelectionSync()
32 | panel.onDidDispose(() => {
33 | selectionListener.dispose()
34 | })
35 | setupMessageListeners()
36 | }
37 | })
38 |
39 | vscode.commands.registerCommand('polacode.activate', () => {
40 | panel = vscode.window.createWebviewPanel('polacode', P_TITLE, 2, {
41 | enableScripts: true,
42 | localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, 'webview'))]
43 | })
44 |
45 | panel.webview.html = getHtmlContent(htmlPath)
46 |
47 | const selectionListener = setupSelectionSync()
48 | panel.onDidDispose(() => {
49 | selectionListener.dispose()
50 | })
51 |
52 | setupMessageListeners()
53 |
54 | const fontFamily = vscode.workspace.getConfiguration('editor').fontFamily
55 | const bgColor = context.globalState.get('polacode.bgColor', '#2e3440')
56 | panel.webview.postMessage({
57 | type: 'init',
58 | fontFamily,
59 | bgColor
60 | })
61 |
62 | syncSettings()
63 | })
64 |
65 | vscode.workspace.onDidChangeConfiguration(e => {
66 | if (e.affectsConfiguration('polacode') || e.affectsConfiguration('editor')) {
67 | syncSettings()
68 | }
69 | })
70 |
71 | function setupMessageListeners() {
72 | panel.webview.onDidReceiveMessage(({ type, data }) => {
73 | switch (type) {
74 | case 'shoot':
75 | vscode.window
76 | .showSaveDialog({
77 | defaultUri: lastUsedImageUri,
78 | filters: {
79 | Images: ['png']
80 | }
81 | })
82 | .then(uri => {
83 | if (uri) {
84 | writeSerializedBlobToFile(data.serializedBlob, uri.fsPath)
85 | lastUsedImageUri = uri
86 | }
87 | })
88 | break
89 | case 'getAndUpdateCacheAndSettings':
90 | panel.webview.postMessage({
91 | type: 'restoreBgColor',
92 | bgColor: context.globalState.get('polacode.bgColor', '#2e3440')
93 | })
94 |
95 | syncSettings()
96 | break
97 | case 'updateBgColor':
98 | context.globalState.update('polacode.bgColor', data.bgColor)
99 | break
100 | case 'invalidPasteContent':
101 | vscode.window.showInformationMessage(
102 | 'Pasted content is invalid. Only copy from VS Code and check if your shortcuts for copy/paste have conflicts.'
103 | )
104 | break
105 | }
106 | })
107 | }
108 |
109 | function syncSettings() {
110 | const settings = vscode.workspace.getConfiguration('polacode')
111 | const editorSettings = vscode.workspace.getConfiguration('editor', null)
112 | panel.webview.postMessage({
113 | type: 'updateSettings',
114 | shadow: settings.get('shadow'),
115 | transparentBackground: settings.get('transparentBackground'),
116 | backgroundColor: settings.get('backgroundColor'),
117 | target: settings.get('target'),
118 | ligature: editorSettings.get('fontLigatures')
119 | })
120 | }
121 |
122 | function setupSelectionSync() {
123 | return vscode.window.onDidChangeTextEditorSelection(e => {
124 | if (e.selections[0] && !e.selections[0].isEmpty) {
125 | vscode.commands.executeCommand('editor.action.clipboardCopyWithSyntaxHighlightingAction')
126 | panel.postMessage({
127 | type: 'update'
128 | })
129 | }
130 | })
131 | }
132 | }
133 |
134 | function getHtmlContent(htmlPath) {
135 | const htmlContent = fs.readFileSync(htmlPath, 'utf-8')
136 | return htmlContent.replace(/script src="([^"]*)"/g, (match, src) => {
137 | const realSource = 'vscode-resource:' + path.resolve(htmlPath, '..', src)
138 | return `script src="${realSource}"`
139 | })
140 | }
141 |
142 | exports.activate = activate
143 |
--------------------------------------------------------------------------------
/webview/dom2image.js:
--------------------------------------------------------------------------------
1 | /*! dom-to-image 10-06-2017 */
2 | !function(a){"use strict";function b(a,b){function c(a){return b.bgcolor&&(a.style.backgroundColor=b.bgcolor),b.width&&(a.style.width=b.width+"px"),b.height&&(a.style.height=b.height+"px"),b.style&&Object.keys(b.style).forEach(function(c){a.style[c]=b.style[c]}),a}return b=b||{},g(b),Promise.resolve(a).then(function(a){return i(a,b.filter,!0)}).then(j).then(k).then(c).then(function(c){return l(c,b.width||q.width(a),b.height||q.height(a))})}function c(a,b){return h(a,b||{}).then(function(b){return b.getContext("2d").getImageData(0,0,q.width(a),q.height(a)).data})}function d(a,b){return h(a,b||{}).then(function(a){return a.toDataURL()})}function e(a,b){return b=b||{},h(a,b).then(function(a){return a.toDataURL("image/jpeg",b.quality||1)})}function f(a,b){return h(a,b||{}).then(q.canvasToBlob)}function g(a){"undefined"==typeof a.imagePlaceholder?v.impl.options.imagePlaceholder=u.imagePlaceholder:v.impl.options.imagePlaceholder=a.imagePlaceholder,"undefined"==typeof a.cacheBust?v.impl.options.cacheBust=u.cacheBust:v.impl.options.cacheBust=a.cacheBust}function h(a,c){function d(a){var b=document.createElement("canvas");if(b.width=c.width||q.width(a),b.height=c.height||q.height(a),c.bgcolor){var d=b.getContext("2d");d.fillStyle=c.bgcolor,d.fillRect(0,0,b.width,b.height)}return b}return b(a,c).then(q.makeImage).then(q.delay(100)).then(function(b){var c=d(a);return c.getContext("2d").drawImage(b,0,0),c})}function i(a,b,c){function d(a){return a instanceof HTMLCanvasElement?q.makeImage(a.toDataURL()):a.cloneNode(!1)}function e(a,b,c){function d(a,b,c){var d=Promise.resolve();return b.forEach(function(b){d=d.then(function(){return i(b,c)}).then(function(b){b&&a.appendChild(b)})}),d}var e=a.childNodes;return 0===e.length?Promise.resolve(b):d(b,q.asArray(e),c).then(function(){return b})}function f(a,b){function c(){function c(a,b){function c(a,b){q.asArray(a).forEach(function(c){b.setProperty(c,a.getPropertyValue(c),a.getPropertyPriority(c))})}a.cssText?b.cssText=a.cssText:c(a,b)}c(window.getComputedStyle(a),b.style)}function d(){function c(c){function d(a,b,c){function d(a){var b=a.getPropertyValue("content");return a.cssText+" content: "+b+";"}function e(a){function b(b){return b+": "+a.getPropertyValue(b)+(a.getPropertyPriority(b)?" !important":"")}return q.asArray(a).map(b).join("; ")+";"}var f="."+a+":"+b,g=c.cssText?d(c):e(c);return document.createTextNode(f+"{"+g+"}")}var e=window.getComputedStyle(a,c),f=e.getPropertyValue("content");if(""!==f&&"none"!==f){var g=q.uid();b.className=b.className+" "+g;var h=document.createElement("style");h.appendChild(d(g,c,e)),b.appendChild(h)}}[":before",":after"].forEach(function(a){c(a)})}function e(){a instanceof HTMLTextAreaElement&&(b.innerHTML=a.value),a instanceof HTMLInputElement&&b.setAttribute("value",a.value)}function f(){b instanceof SVGElement&&(b.setAttribute("xmlns","http://www.w3.org/2000/svg"),b instanceof SVGRectElement&&["width","height"].forEach(function(a){var c=b.getAttribute(a);c&&b.style.setProperty(a,c)}))}return b instanceof Element?Promise.resolve().then(c).then(d).then(e).then(f).then(function(){return b}):b}return c||!b||b(a)?Promise.resolve(a).then(d).then(function(c){return e(a,c,b)}).then(function(b){return f(a,b)}):Promise.resolve()}function j(a){return s.resolveAll().then(function(b){var c=document.createElement("style");return a.appendChild(c),c.appendChild(document.createTextNode(b)),a})}function k(a){return t.inlineAll(a).then(function(){return a})}function l(a,b,c){return Promise.resolve(a).then(function(a){return a.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(a)}).then(q.escapeXhtml).then(function(a){return''+a+""}).then(function(a){return'"}).then(function(a){return"data:image/svg+xml;charset=utf-8,"+a})}function m(){function a(){var a="application/font-woff",b="image/jpeg";return{woff:a,woff2:a,ttf:"application/font-truetype",eot:"application/vnd.ms-fontobject",png:"image/png",jpg:b,jpeg:b,gif:"image/gif",tiff:"image/tiff",svg:"image/svg+xml"}}function b(a){var b=/\.([^\.\/]*?)$/g.exec(a);return b?b[1]:""}function c(c){var d=b(c).toLowerCase();return a()[d]||""}function d(a){return a.search(/^(data:)/)!==-1}function e(a){return new Promise(function(b){for(var c=window.atob(a.toDataURL().split(",")[1]),d=c.length,e=new Uint8Array(d),f=0;f
2 |
3 |
7 |
70 |
71 |
72 |
73 |
74 |
console.log('0. Run command `Polacode 📸 `')
console.log('1. Copy some code')
console.log('2. Paste into Polacode view')
console.log('3. Click the button 📸 ')
75 |
76 |
77 |
78 |
79 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |