From bacddc78219e08e269f3b888c86c559ca5f33cc9 Mon Sep 17 00:00:00 2001
From: George <31376482+george-gca@users.noreply.github.com>
Date: Sun, 19 Mar 2023 18:17:34 -0300
Subject: [PATCH] Implemented copy code button feature (#1267)
Implemented support for copy code button in code blocks (#1262), also updated blog post about code to reflect it.
---
_includes/scripts/misc.html | 1 +
_posts/2015-07-15-code.md | 32 +++-
_sass/_base.scss | 304 ++++++++++++++++++++++++++----------
assets/js/copy_code.js | 36 +++++
4 files changed, 286 insertions(+), 87 deletions(-)
create mode 100644 assets/js/copy_code.js
diff --git a/_includes/scripts/misc.html b/_includes/scripts/misc.html
index 08ba49f03517..90360808d291 100644
--- a/_includes/scripts/misc.html
+++ b/_includes/scripts/misc.html
@@ -12,3 +12,4 @@
+
diff --git a/_posts/2015-07-15-code.md b/_posts/2015-07-15-code.md
index 675543df5eda..a86fa077c536 100644
--- a/_posts/2015-07-15-code.md
+++ b/_posts/2015-07-15-code.md
@@ -9,7 +9,37 @@ categories: sample-posts
This theme implements a built-in Jekyll feature, the use of Rouge, for syntax highlighting.
It supports more than 100 languages.
This example is in C++.
-All you have to do is wrap your code in a liquid tag:
+All you have to do is wrap your code in markdown code tags:
+
+````markdown
+```c++
+code code code
+```
+````
+
+```c++
+int main(int argc, char const \*argv[])
+{
+ string myString;
+
+ cout << "input a string: ";
+ getline(cin, myString);
+ int length = myString.length();
+
+ char charArray = new char * [length];
+
+ charArray = myString;
+ for(int i = 0; i < length; ++i){
+ cout << charArray[i] << " ";
+ }
+
+ return 0;
+}
+```
+
+By default, it does not display line numbers. If you want to display line numbers for every code block, you can set `kramdown.syntax_highlighter_opts.block.line_numbers` to true in your `_config.yml` file.
+
+If you want to display line numbers for a specific code block, all you have to do is wrap your code in a liquid tag:
{% raw %}
{% highlight c++ linenos %}
code code code
{% endhighlight %}
diff --git a/_sass/_base.scss b/_sass/_base.scss
index 8954b6c80287..ec6a768fa713 100644
--- a/_sass/_base.scss
+++ b/_sass/_base.scss
@@ -4,7 +4,18 @@
// Typography
-p, h1, h2, h3, h4, h5, h6, em, div, li, span, strong {
+p,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+em,
+div,
+li,
+span,
+strong {
color: var(--global-text-color);
}
@@ -13,26 +24,33 @@ hr {
}
table {
- td, th {
+
+ td,
+ th {
color: var(--global-text-color);
}
+
td {
font-size: 1rem;
}
}
-a, table.table a {
+a,
+table.table a {
color: var(--global-theme-color);
+
&:hover {
color: var(--global-theme-color);
text-decoration: underline;
}
+
&:hover:after :not(.nav-item.dropdown) {
width: 100%;
}
}
-figure, img {
+figure,
+img {
max-width: 90vw;
}
@@ -86,7 +104,8 @@ blockquote {
// Citation
-.citation, .citation-number {
+.citation,
+.citation-number {
color: var(--global-theme-color);
}
@@ -99,24 +118,30 @@ blockquote {
margin-bottom: 5px;
margin-top: 5px;
font-family: monospace;
+
p {
display: inline-block;
margin: 0;
}
}
}
-.profile.float-right{
+
+.profile.float-right {
margin-left: 1rem;
}
-.profile.float-left{
+
+.profile.float-left {
margin-right: 1rem;
}
@media (min-width: 576px) {
.profile {
width: 30%;
+
.address {
- p { display: block; }
+ p {
+ display: block;
+ }
}
}
}
@@ -124,8 +149,10 @@ blockquote {
.post-description {
margin-bottom: 2rem;
font-size: 0.875rem;
+
a {
color: inherit;
+
&:hover {
color: var(--global-theme-color);
text-decoration: none;
@@ -142,58 +169,73 @@ blockquote {
background-color: var(--global-bg-color);
opacity: 0.95;
}
+
.navbar .dropdown-menu {
background-color: var(--global-bg-color);
border: 1px solid var(--global-divider-color);
+
a:not(.active) {
color: var(--global-text-color);
}
+
a:hover {
color: var(--global-hover-color);
}
+
.dropdown-divider {
border-top: 1px solid var(--global-divider-color) !important;
}
}
+
.dropdown-item {
color: var(--global-text-color);
- &:hover {
- color: var(--global-hover-color);
- background-color: var(--global-bg-color);
- }
+
+ &:hover {
+ color: var(--global-hover-color);
+ background-color: var(--global-bg-color);
+ }
}
+
.navbar.navbar-light {
a {
&:hover {
text-decoration: none;
}
}
+
.navbar-brand {
color: var(--global-text-color);
}
+
.navbar-nav .nav-item .nav-link {
color: var(--global-text-color);
+
&:hover {
color: var(--global-hover-color);
}
}
+
.navbar-nav .nav-item.active>.nav-link {
- background-color: inherit;
- font-weight: bolder;
- color: var(--global-theme-color);
- &:hover {
- color: var(--global-hover-color);
- }
+ background-color: inherit;
+ font-weight: bolder;
+ color: var(--global-theme-color);
+
+ &:hover {
+ color: var(--global-hover-color);
+ }
}
+
.navbar-brand.social {
padding-bottom: 0;
padding-top: 0;
font-size: 1.7rem;
+
a {
i::before {
color: var(--global-text-color);
transition-property: all 0.2s ease-in-out;
}
+
&:hover {
i::before {
color: var(--global-theme-color);
@@ -213,13 +255,16 @@ blockquote {
margin-bottom: 4px;
transition: all 0.2s;
}
+
.top-bar {
transform: rotate(45deg);
transform-origin: 10% 10%;
}
+
.middle-bar {
opacity: 0;
}
+
.bottom-bar {
transform: rotate(-45deg);
transform-origin: 10% 90%;
@@ -230,9 +275,11 @@ blockquote {
.top-bar {
transform: rotate(0);
}
+
.middle-bar {
opacity: 1;
}
+
.bottom-bar {
transform: rotate(0);
}
@@ -243,6 +290,7 @@ blockquote {
border: 0;
background-color: inherit;
color: var(--global-text-color);
+
&:hover {
color: var(--global-hover-color);
}
@@ -252,13 +300,16 @@ blockquote {
.social {
text-align: center;
+
.contact-icons {
font-size: 4rem;
+
a {
i::before {
color: var(--global-text-color);
transition-property: all 0.2s ease-in-out;
}
+
&:hover {
i::before {
color: var(--global-theme-color);
@@ -266,6 +317,7 @@ blockquote {
}
}
}
+
.contact-note {
font-size: 0.8rem;
}
@@ -276,13 +328,16 @@ blockquote {
footer.fixed-bottom {
background-color: var(--global-footer-bg-color);
font-size: 0.75rem;
+
.container {
color: var(--global-footer-text-color);
padding-top: 9px;
padding-bottom: 8px;
}
+
a {
color: var(--global-footer-link-color);
+
&:hover {
color: var(--global-theme-color);
text-decoration: none;
@@ -301,11 +356,11 @@ footer.sticky-bottom {
.cv {
margin-bottom: 40px;
-
+
.card {
background-color: var(--global-card-bg-color);
border: 1px solid var(--global-divider-color);
-
+
.list-group-item {
background-color: inherit;
border-color: var(--global-divider-color);
@@ -333,6 +388,7 @@ footer.sticky-bottom {
text-align: center;
padding-top: 2rem;
padding-bottom: 3rem;
+
h1 {
color: var(--global-theme-color);
font-size: 5rem;
@@ -348,7 +404,8 @@ footer.sticky-bottom {
justify-content: center;
display: flow-root;
- p, li {
+ p,
+ li {
list-style: none;
display: inline-block;
padding: 1rem 0.5rem;
@@ -361,25 +418,30 @@ footer.sticky-bottom {
margin: 0;
margin-bottom: 40px;
padding: 0;
+
li {
border-bottom: 1px solid var(--global-divider-color);
list-style: none;
padding-top: 2rem;
padding-bottom: 2rem;
+
.post-meta {
color: var(--global-text-color-light);
font-size: 0.875rem;
margin-bottom: 0;
}
+
.post-tags {
color: var(--global-text-color-light);
font-size: 0.875rem;
padding-top: 0.25rem;
padding-bottom: 0;
}
+
a {
color: var(--global-text-color);
text-decoration: none;
+
&:hover {
color: var(--global-theme-color);
}
@@ -391,13 +453,16 @@ footer.sticky-bottom {
.page-item {
.page-link {
color: var(--global-text-color);
+
&:hover {
color: $black-color;
}
}
+
&.active .page-link {
color: $white-color;
background-color: var(--global-theme-color);
+
&:hover {
background-color: var(--global-theme-color);
}
@@ -445,7 +510,8 @@ footer.sticky-bottom {
}
}
- .grid-sizer, .grid-item {
+ .grid-sizer,
+ .grid-item {
width: 250px;
margin-bottom: 10px;
}
@@ -465,6 +531,7 @@ footer.sticky-bottom {
.publications {
margin-top: 2rem;
+
h1 {
color: var(--global-theme-color);
font-size: 2rem;
@@ -472,12 +539,15 @@ footer.sticky-bottom {
margin-top: 1em;
margin-bottom: 1em;
}
+
h2 {
margin-bottom: 1rem;
+
span {
font-size: 1.5rem;
}
}
+
h2.year {
color: var(--global-divider-color);
border-top: 1px solid var(--global-divider-color);
@@ -486,6 +556,7 @@ footer.sticky-bottom {
margin-bottom: -2rem;
text-align: right;
}
+
ol.bibliography {
list-style: none;
padding: 0;
@@ -493,56 +564,69 @@ footer.sticky-bottom {
li {
margin-bottom: 1rem;
+
.preview {
width: 100%;
min-width: 80px;
max-width: 200px;
}
+
.abbr {
height: 2rem;
margin-bottom: 0.5rem;
+
abbr {
display: inline-block;
background-color: var(--global-theme-color);
padding-left: 1rem;
padding-right: 1rem;
+
a {
color: white;
+
&:hover {
text-decoration: none;
}
}
}
+
.award {
color: var(--global-theme-color) !important;
border: 1px solid var(--global-theme-color);
}
}
+
.title {
font-weight: bolder;
}
+
.author {
a {
border-bottom: 1px dashed var(--global-theme-color);
+
&:hover {
- border-bottom-style: solid;
- text-decoration: none;
+ border-bottom-style: solid;
+ text-decoration: none;
}
}
- > em {
+
+ >em {
border-bottom: 1px solid;
font-style: normal;
}
- > span.more-authors {
+
+ >span.more-authors {
color: var(--global-text-color-light);
border-bottom: 1px dashed var(--global-text-color-light);
cursor: pointer;
+
&:hover {
- color: var(--global-text-color);
- border-bottom: 1px dashed var(--global-text-color);
+ color: var(--global-text-color);
+ border-bottom: 1px dashed var(--global-text-color);
}
}
}
+
.links {
a.btn {
color: var(--global-text-color);
@@ -551,12 +635,14 @@ footer.sticky-bottom {
padding-right: 1rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
+
&:hover {
color: var(--global-theme-color);
border-color: var(--global-theme-color);
}
}
}
+
.badges {
span {
display: inline-block;
@@ -564,11 +650,13 @@ footer.sticky-bottom {
height: 100%;
padding-left: 0.5rem;
vertical-align: middle;
+
&:hover {
text-decoration: underline;
}
}
}
+
.hidden {
font-size: 0.875rem;
max-height: 0px;
@@ -584,12 +672,14 @@ footer.sticky-bottom {
line-height: 1.4em;
margin: 10px;
}
+
pre {
font-size: 1em;
line-height: 1.4em;
padding: 10px;
}
}
+
.hidden.open {
max-height: 100em;
transition-property: 0.15s ease;
@@ -598,9 +688,11 @@ footer.sticky-bottom {
-o-transition: 0.15s ease;
transition: all 0.15s ease;
}
+
div.abstract.hidden {
border: dashed 1px var(--global-bg-color);
}
+
div.abstract.hidden.open {
border-color: var(--global-text-color);
}
@@ -618,9 +710,12 @@ pre {
background-color: var(--global-code-bg-color);
border-radius: 6px;
padding: 6px 12px;
- pre, code {
+
+ pre,
+ code {
background-color: transparent;
border-radius: 0;
+ margin-bottom: 0;
padding: 0;
}
}
@@ -643,26 +738,30 @@ html.transition *:after {
}
// Extra Markdown style (post Customization)
-.post{
- .post-meta{
+.post {
+ .post-meta {
color: var(--global-text-color-light);
font-size: 0.875rem;
margin-bottom: 0;
}
- .post-tags{
+
+ .post-tags {
color: var(--global-text-color-light);
font-size: 0.875rem;
padding-top: 0.25rem;
padding-bottom: 1rem;
+
a {
color: var(--global-text-color-light);
text-decoration: none;
+
&:hover {
color: var(--global-theme-color);
}
}
}
- .post-content{
+
+ .post-content {
blockquote {
border-left: 5px solid var(--global-theme-color);
padding: 8px;
@@ -671,56 +770,89 @@ html.transition *:after {
}
progress {
- /* Positioning */
- position: fixed;
- left: 0;
- top: 56px;
- z-index: 10;
-
- /* Dimensions */
- width: 100%;
- height: 1px;
-
- /* Reset the appearance */
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
-
- /* Get rid of the default border in Firefox/Opera. */
- border: none;
-
- /* Progress bar container for Firefox/IE10 */
- background-color: transparent;
-
- /* Progress bar value for IE10 */
- color: var(--global-theme-color);
- }
-
- progress::-webkit-progress-bar {
- background-color: transparent;
- }
-
- progress::-webkit-progress-value {
- background-color: var(--global-theme-color);
- }
-
- progress::-moz-progress-bar {
- background-color: var(--global-theme-color);
- }
-
- .progress-container {
- width: 100%;
- background-color: transparent;
- position: fixed;
- top: 56px;
- left: 0;
- height: 5px;
- display: block;
- }
-
- .progress-bar {
- background-color: var(--global-theme-color);
- width: 0%;
- display: block;
- height: inherit;
- }
+ /* Positioning */
+ position: fixed;
+ left: 0;
+ top: 56px;
+ z-index: 10;
+
+ /* Dimensions */
+ width: 100%;
+ height: 1px;
+
+ /* Reset the appearance */
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+
+ /* Get rid of the default border in Firefox/Opera. */
+ border: none;
+
+ /* Progress bar container for Firefox/IE10 */
+ background-color: transparent;
+
+ /* Progress bar value for IE10 */
+ color: var(--global-theme-color);
+}
+
+progress::-webkit-progress-bar {
+ background-color: transparent;
+}
+
+progress::-webkit-progress-value {
+ background-color: var(--global-theme-color);
+}
+
+progress::-moz-progress-bar {
+ background-color: var(--global-theme-color);
+}
+
+.progress-container {
+ width: 100%;
+ background-color: transparent;
+ position: fixed;
+ top: 56px;
+ left: 0;
+ height: 5px;
+ display: block;
+}
+
+.progress-bar {
+ background-color: var(--global-theme-color);
+ width: 0%;
+ display: block;
+ height: inherit;
+}
+
+pre {
+ padding: 8px 12px;
+ position: relative;
+
+ /* Copy code to clipboard button */
+ .copy {
+ background: var(--global-card-bg-color);
+ border-color: var(--global-bg-color);
+ border-radius: .3rem;
+ border-style: solid;
+ color: var(--global-text-color);
+ font-size: medium;
+ opacity: 0;
+ position: absolute;
+ right: .25rem;
+ top: .25rem;
+
+ &:active,
+ &:focus,
+ &:hover {
+ color: var(--global-hover-color);
+ opacity: 1;
+ }
+ }
+
+ &:active .copy,
+ &:focus .copy,
+ &:hover .copy {
+ color: var(--global-hover-color);
+ opacity: 1;
+ }
+}
diff --git a/assets/js/copy_code.js b/assets/js/copy_code.js
new file mode 100644
index 000000000000..b12af5342b07
--- /dev/null
+++ b/assets/js/copy_code.js
@@ -0,0 +1,36 @@
+// create element for copy button in code blocks
+var codeBlocks = document.querySelectorAll('pre');
+codeBlocks.forEach(function (codeBlock) {
+ if (codeBlock.querySelector('pre:not(.lineno)') || codeBlock.querySelector('code')) {
+ var copyButton = document.createElement('button');
+ copyButton.className = 'copy';
+ copyButton.type = 'button';
+ copyButton.ariaLabel = 'Copy code to clipboard';
+ copyButton.innerText = 'Copy';
+ copyButton.innerHTML = '';
+ codeBlock.append(copyButton);
+
+ // get code from code block and copy to clipboard
+ copyButton.addEventListener('click', function () {
+ // check if code block has line numbers
+ // i.e. `kramdown.syntax_highlighter_opts.block.line_numbers` set to true in _config.yml
+ // or using `jekyll highlight` liquid tag with `linenos` option
+ if (codeBlock.querySelector('pre:not(.lineno)')) {
+ // get code from code block ignoring line numbers
+ var code = codeBlock.querySelector('pre:not(.lineno)').innerText.trim();
+ } else { // if (codeBlock.querySelector('code')) {
+ // get code from code block when line numbers are not displayed
+ var code = codeBlock.querySelector('code').innerText.trim();
+ }
+ window.navigator.clipboard.writeText(code);
+ copyButton.innerText = 'Copied';
+ copyButton.innerHTML = '';
+ var waitFor = 3000;
+
+ setTimeout(function () {
+ copyButton.innerText = 'Copy';
+ copyButton.innerHTML = '';
+ }, waitFor);
+ });
+ }
+});