Skip to content

Added static site#9

Merged
tutunak merged 3 commits intomasterfrom
added-static-site
Feb 1, 2026
Merged

Added static site#9
tutunak merged 3 commits intomasterfrom
added-static-site

Conversation

@tutunak
Copy link
Copy Markdown
Owner

@tutunak tutunak commented Feb 1, 2026

Summary by CodeRabbit

  • Documentation
    • Launched a new product website with a dark-themed, responsive design.
    • Added interactive terminal demo with typewriter animation and copy-to-clipboard install command.
    • Included tabbed installation guides, feature overview, workflow examples, and footer links.
    • Published site to jcli.tutunak.com.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 1, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a complete static website for jcli: domain config, HTML landing page, dark-themed responsive CSS, and JS for a terminal typewriter, tab switching, and copy-to-clipboard interactions.

Changes

Cohort / File(s) Summary
Domain config
docs/CNAME
Adds CNAME file containing jcli.tutunak.com.
Static site HTML
docs/index.html
New landing page with header, hero, demo terminal, installation tabs, features, workflow, and footer; references CSS and JS assets.
Styling
docs/css/style.css
Adds a comprehensive dark theme stylesheet: variables, layout, components, responsive rules, faux terminal styling, copy button styles, and animations.
Client behavior
docs/js/terminal.js
Adds typewriter/terminal animation, sequence control, tab switching logic, and copy-to-clipboard helper with small UI feedback.

Sequence Diagram(s)

mermaid
sequenceDiagram
actor User
participant Browser
participant terminalJS as terminal.js
participant ClipboardAPI as Clipboard
User->>Browser: Clicks copy/install or switches tab
Browser->>terminalJS: Dispatch event (click/tab)
terminalJS->>Browser: Update DOM (highlight, toggle tab, start/stop typing)
terminalJS->>ClipboardAPI: navigator.clipboard.writeText() (on copy)
ClipboardAPI-->>terminalJS: Promise resolved/rejected
terminalJS->>Browser: Show copied feedback / resume animation

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble keys with tiny paws,
A glowing terminal without a pause.
Tabs that flip and text that types,
A dark theme stitched with tiny stripes.
Hooray — jcli hops into the light! 🐇✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Added static site' accurately summarizes the main change: introducing a complete static website with HTML, CSS, JavaScript, and configuration files for the jcli product.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch added-static-site

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@docs/index.html`:
- Line 29: Replace the incorrect Go module path in the install command: update
the <code id="install-cmd"> value from "go install
github.com/tutuank/jcli@latest" to use the correct GitHub username "tutunak" so
the string becomes "go install github.com/tutunak/jcli@latest"; ensure the <code
id="install-cmd"> element text matches other references (e.g., line 105/GitHub
URLs).

In `@docs/js/terminal.js`:
- Around line 76-83: The copyInstall function currently calls
navigator.clipboard.writeText(cmd) without handling promise rejections; update
copyInstall to attach a .catch handler to navigator.clipboard.writeText(cmd) to
handle failures (e.g., permission denied or API unavailable), and in the catch:
log the error (console.error) and provide UI feedback (use the same button
element queried as btn — e.g., add a distinct error class like 'copy-error' or
change text content, then remove it with setTimeout similar to the success
path). Also consider a graceful fallback path when navigator.clipboard is
undefined (e.g., fallback to creating a temporary textarea and
document.execCommand('copy') or display instructions to manually copy).
🧹 Nitpick comments (3)
docs/css/style.css (1)

36-45: Consider adding a fallback for backdrop-filter.

While backdrop-filter has good modern browser support, older browsers will show no blur effect. The semi-transparent background color provides a reasonable fallback, so this is acceptable as-is.

docs/js/terminal.js (2)

26-30: DOM queries execute before DOMContentLoaded.

The getElementById and querySelector calls on lines 26-27 run immediately when the script loads. Since the script is placed at the end of the body, this should work, but moving these inside the DOMContentLoaded callback would be more robust and explicit.

♻️ Suggested refactor
-const terminalOutput = document.getElementById('terminal-output');
-const cursor = document.querySelector('.cursor');
 let currentSequence = 0;
 let currentChar = 0;
 let isTyping = false;
+let terminalOutput;
+let cursor;

 // ... (keep typeWriter and typeCharacter functions)

 // Start animation when page loads
 document.addEventListener('DOMContentLoaded', () => {
+    terminalOutput = document.getElementById('terminal-output');
+    cursor = document.querySelector('.cursor');
     setTimeout(typeWriter, 500);
 });

86-94: Static analysis hint is a false positive.

Biome flags lines 88-89 for returning values from forEach callbacks, but classList.remove() and classList.add() return undefined, not meaningful values. The code is functionally correct. However, using for...of loops would be slightly more idiomatic and avoid the lint warning.

♻️ Optional refactor to avoid lint warning
 // Tab switching
 document.querySelectorAll('.tab').forEach(tab => {
     tab.addEventListener('click', () => {
-        document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
-        document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
+        for (const t of document.querySelectorAll('.tab')) {
+            t.classList.remove('active');
+        }
+        for (const c of document.querySelectorAll('.tab-content')) {
+            c.classList.remove('active');
+        }

         tab.classList.add('active');
         document.getElementById(tab.dataset.tab).classList.add('active');
     });
 });

@tutunak tutunak merged commit 2e4abfd into master Feb 1, 2026
7 of 8 checks passed
@tutunak tutunak deleted the added-static-site branch February 1, 2026 10:15
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a static marketing website for the jcli CLI tool, hosted on GitHub Pages. The site provides an interactive terminal demo, installation instructions, and feature highlights to help users understand and adopt the tool.

Changes:

  • Added a complete static website with HTML, CSS, and JavaScript
  • Implemented an animated terminal demonstration showcasing the CLI workflow
  • Configured GitHub Pages with custom domain (jcli.tutunak.com)

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
docs/index.html Main landing page with hero section, features, installation guide, and workflow documentation
docs/js/terminal.js Interactive typewriter animation for terminal demo and UI interaction handlers
docs/css/style.css Complete styling with dark theme, responsive design, and GitHub-inspired aesthetics
docs/assets/favicon.svg Simple SVG favicon with monospace "j>" branding
docs/CNAME GitHub Pages custom domain configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +30 to +31
let isTyping = false;

Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable isTyping is declared but never used. It appears to be leftover from development. Consider removing it or implementing it if it was meant to prevent multiple simultaneous animations.

Suggested change
let isTyping = false;

Copilot uses AI. Check for mistakes.
<p class="tagline">Select issues. Track work. Generate branches. Stay in flow.</p>
<div class="install-box">
<code id="install-cmd">go install github.com/tutunak/jcli@latest</code>
<button class="copy-btn" onclick="copyInstall()" aria-label="Copy install command">
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using inline event handlers (onclick="copyInstall()") is considered a security and maintainability concern. It violates Content Security Policy (CSP) best practices and makes the code harder to maintain. Consider moving this to an event listener in the JavaScript file, similar to how the tab switching is implemented (lines 86-94 in terminal.js).

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +92
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));

tab.classList.add('active');
document.getElementById(tab.dataset.tab).classList.add('active');
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tab buttons are missing keyboard accessibility. When using keyboard navigation, users should be able to activate tabs with both Enter and Space keys, and arrow keys should navigate between tabs. Consider adding appropriate keyboard event handlers (keydown/keypress) alongside the click handler to ensure full keyboard accessibility.

Suggested change
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
tab.classList.add('active');
document.getElementById(tab.dataset.tab).classList.add('active');
function activateTab(tab, tabs) {
tabs.forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
tab.classList.add('active');
var targetId = tab.dataset.tab;
if (targetId) {
var target = document.getElementById(targetId);
if (target) {
target.classList.add('active');
}
}
}
const tabs = document.querySelectorAll('.tab');
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => {
activateTab(tab, tabs);
});
tab.addEventListener('keydown', (event) => {
const key = event.key;
if (key === 'Enter' || key === ' ') {
event.preventDefault();
activateTab(tab, tabs);
return;
}
let newIndex = -1;
if (key === 'ArrowRight' || key === 'ArrowDown') {
event.preventDefault();
newIndex = (index + 1) % tabs.length;
} else if (key === 'ArrowLeft' || key === 'ArrowUp') {
event.preventDefault();
newIndex = (index - 1 + tabs.length) % tabs.length;
}
if (newIndex !== -1) {
const newTab = tabs[newIndex];
newTab.focus();
activateTab(newTab, tabs);
}

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +82
navigator.clipboard.writeText(cmd).then(() => {
const btn = document.querySelector('.copy-btn');
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 2000);
});
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clipboard write operation lacks error handling. If navigator.clipboard.writeText() fails (e.g., due to permissions or browser compatibility), the promise rejection is silently ignored. Consider adding a .catch() handler to gracefully handle failures and potentially show an error message to the user.

Suggested change
navigator.clipboard.writeText(cmd).then(() => {
const btn = document.querySelector('.copy-btn');
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 2000);
});
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
navigator.clipboard.writeText(cmd).then(() => {
const btn = document.querySelector('.copy-btn');
if (btn) {
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 2000);
}
}).catch((err) => {
// Optional: log the error for debugging; could also show a user-facing message here
console.error('Failed to copy to clipboard:', err);
});
} else {
// Clipboard API not available; optional: provide a fallback or user notification
console.error('Clipboard API is not available in this browser.');
}

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +79
const cmd = document.getElementById('install-cmd').textContent;
navigator.clipboard.writeText(cmd).then(() => {
const btn = document.querySelector('.copy-btn');
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for DOM element selection. If the element with id 'install-cmd' doesn't exist, document.getElementById('install-cmd') will return null, and accessing .textContent on null will throw a TypeError. Consider adding a null check before accessing the textContent property.

Suggested change
const cmd = document.getElementById('install-cmd').textContent;
navigator.clipboard.writeText(cmd).then(() => {
const btn = document.querySelector('.copy-btn');
const installCmdElement = document.getElementById('install-cmd');
if (!installCmdElement) {
return;
}
const cmd = installCmdElement.textContent || '';
navigator.clipboard.writeText(cmd).then(() => {
const btn = document.querySelector('.copy-btn');
if (!btn) {
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +67
if (currentChar === 0 && seq.delay > 0) {
setTimeout(() => {
currentChar = 0;
typeCharacter();
}, seq.delay);
} else {
typeCharacter();
}
}

function typeCharacter() {
const seq = sequences[currentSequence];

if (currentChar < seq.text.length) {
const span = document.createElement('span');
span.className = seq.class || '';
span.textContent = seq.text[currentChar];
terminalOutput.appendChild(span);
currentChar++;

const charDelay = seq.delay || 30;
setTimeout(typeCharacter, charDelay);
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delay property in the sequences array serves dual purposes: as an initial delay before typing a sequence (line 46) and as the per-character typing delay (line 66). This creates confusion and makes the code harder to maintain. Consider separating these into two distinct properties, such as initialDelay and charDelay, to make the intent clearer. Also, line 48 redundantly sets currentChar = 0 when it's already 0.

Copilot uses AI. Check for mistakes.
<h1>Jira without leaving your terminal.</h1>
<p class="tagline">Select issues. Track work. Generate branches. Stay in flow.</p>
<div class="install-box">
<code id="install-cmd">go install github.com/tutunak/jcli@latest</code>
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GitHub repository path has a typo. It should be "github.com/tutunak/jcli" (with 'n' after 'tutua'), not "github.com/tutuank/jcli". This is inconsistent with the actual repository path used throughout the codebase (see cmd/config.go:7, main.go:7, etc.).

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +81
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 2000);
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for DOM element selection. If the element with class 'copy-btn' doesn't exist, document.querySelector('.copy-btn') will return null, and calling .classList.add() on null will throw a TypeError. Consider adding a null check before manipulating the classList.

Suggested change
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 2000);
if (btn) {
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 2000);
}

Copilot uses AI. Check for mistakes.
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));

tab.classList.add('active');
document.getElementById(tab.dataset.tab).classList.add('active');
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check when accessing tab dataset. If tab.dataset.tab is undefined or if document.getElementById(tab.dataset.tab) returns null, calling .classList.add() on null will throw a TypeError. Consider adding checks to ensure both the dataset property exists and the corresponding element is found.

Suggested change
document.getElementById(tab.dataset.tab).classList.add('active');
const contentId = tab.dataset ? tab.dataset.tab : null;
if (!contentId) {
return;
}
const content = document.getElementById(contentId);
if (content) {
content.classList.add('active');
}

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +42
function typeWriter() {
if (currentSequence >= sequences.length) {
// Reset and loop
setTimeout(() => {
terminalOutput.innerHTML = '';
currentSequence = 0;
currentChar = 0;
typeWriter();
}, 3000);
return;
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typewriter animation runs indefinitely without any cleanup mechanism. If the user navigates away from the page and back (in a single-page application context), or if the terminal element is removed from the DOM, the setTimeout callbacks will continue to execute, potentially causing memory leaks. Consider adding a stop/cleanup function that clears pending timeouts, or checking if the terminal element still exists in the DOM before continuing the animation.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants