frontend polishing, added explenations and mutli command halndling

main
czoczo 6 months ago
parent 0ec5dc6c4d
commit 8e4f3482b1
  1. 5
      webpage/BetterBash/index.html
  2. BIN
      webpage/BetterBash/public/favicon.ico
  3. BIN
      webpage/BetterBash/public/logo.png
  4. 353
      webpage/BetterBash/src/App.vue
  5. 47
      webpage/BetterBash/src/App.vue.org
  6. BIN
      webpage/BetterBash/src/assets/bb_ico.ico
  7. BIN
      webpage/BetterBash/src/assets/bb_ico.png
  8. BIN
      webpage/BetterBash/src/assets/logo.png
  9. 1
      webpage/BetterBash/src/assets/logo.svg
  10. 329
      webpage/main.vue

@ -3,8 +3,11 @@
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Tiny5&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>BetterBash</title>
</head>
<body>
<div id="app"></div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

@ -1,8 +1,12 @@
<template>
<div class="container">
<h1>Better Bash</h1>
<div class="customizer">
<div class="header">
<img src="/logo.png" class="logo">
<h1>BetterBash</h1>
<h2>An opinionated attempt to make Bash a little better.</h2>
</div>
<div class="color-controls">
<div
v-for="(label, colorKey) in colorLabels"
@ -11,12 +15,14 @@
>
<div class="color-selector-main">
<label :for="`select-${colorKey}`">
{{ label }}
<div
class="color-preview"
:style="{ backgroundColor: getPreviewColorFromBash(generatedColors[colorKey]) }"
></div>
{{ label }}
</label>
</div>
<div class="color-modifier-checkboxes">
<select :id="`select-${colorKey}`" v-model="selectedColorAttributes[colorKey].baseCode" @change="updatePromptDetails">
<option
v-for="colorOpt in baseColorOptions"
@ -26,8 +32,6 @@
{{ colorOpt.name }}
</option>
</select>
</div>
<div class="color-modifier-checkboxes">
<label :for="`light-${colorKey}`">
<input :id="`light-${colorKey}`" type="checkbox" v-model="selectedColorAttributes[colorKey].isLight" @change="updatePromptDetails" />
Light
@ -41,9 +45,9 @@
<!-- Avatar Toggle Control -->
<div class="color-select-group">
<div class="color-selector-main">
<label for="avatar-toggle">Show Avatar</label>
<div class="color-modifier-checkboxes">
<input id="avatar-toggle" type="checkbox" v-model="showAvatar" @change="updatePromptDetails" />
<label for="avatar-toggle">Show Avatar</label>
</div>
</div>
</div>
@ -88,13 +92,13 @@
<div class="ps1-line">
<span :class="getColorClassFromBash(generatedColors.BORDCOL)"></span
><span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span
><span :class="getColorClassFromBash(generatedColors.PATH_COLOR)">~/repo</span
><span :class="getColorClassFromBash(generatedColors.PATH_COLOR)">~/git_repo</span
><span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span
><span :class="getColorClassFromBash(generatedColors.BORDCOL)"></span
><span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span
><span :class="getColorClassFromBash(generatedColors.PRIMARY_COLOR)">$</span
><span :class="getColorClassFromBash(generatedColors.RST)"> on </span
><span class="text-green">master</span
><span class="text-green">main</span
><span :class="getColorClassFromBash(generatedColors.RST)">=</span
><span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span
><span :class="getColorClassFromBash(generatedColors.BORDCOL)">-&gt;</span>
@ -150,38 +154,172 @@
><span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span
><span :class="getColorClassFromBash(generatedColors.BORDCOL)">-&gt;</span>
</div>
</div>
<div class="share-section">
<h3>Quick install</h3>
<div class="share-url-container">
<input type="text" :value="installUrl" readonly id="installUrlInput" @click="selectUrlText" />
<button @click="copyCmdToClipboard">Copy command</button>
<!-- Tab navigation -->
<div class="tab-navigation">
<button
class="tab-button"
:class="{ active: activeTab === 'curl' }"
@click="activeTab = 'curl'"
>
curl
</button>
<button
class="tab-button"
:class="{ active: activeTab === 'wget' }"
@click="activeTab = 'wget'"
>
wget
</button>
<button
class="tab-button"
:class="{ active: activeTab === 'openssl' }"
@click="activeTab = 'openssl'"
>
openssl
</button>
</div>
<!-- Tab content -->
<div class="tab-content">
<div v-show="activeTab === 'curl'" class="tab-panel">
<div class="share-url-container">
<input type="text" :value="curlInstallUrl" readonly id="installCurlCmd" @click="selectUrlText" />
<button @click="copyCurlCmdToClipboard">Copy command</button>
</div>
</div>
<div v-show="activeTab === 'wget'" class="tab-panel">
<div class="share-url-container">
<input type="text" :value="wgetInstallUrl" readonly id="installWgetCmd" @click="selectUrlText" />
<button @click="copyWgetCmdToClipboard">Copy command</button>
</div>
</div>
<div v-show="activeTab === 'openssl'" class="tab-panel">
<div class="share-url-container multiline">
<textarea :value="opensslInstallUrl" readonly id="installOpensslCmd" @click="selectUrlText" rows="5"></textarea>
<button @click="copyOpensslCmdToClipboard">Copy command</button>
</div>
</div>
</div>
<p v-if="copyCmdSuccess" class="copy-success-message">Copied to clipboard!</p>
<h3>Share Your Theme</h3>
<h3>Share your theme</h3>
<div class="share-url-container">
<input type="text" :value="shareableUrl" readonly id="shareUrlInput" @click="selectUrlText" />
<button @click="copyUrlToClipboard">Copy URL</button>
</div>
<p v-if="copySuccess" class="copy-success-message">Copied to clipboard!</p>
</div>
<!---<h3>Load Theme from URL</h3>
<div class="share-url-container">
<input type="text" v-model="loadUrlInput" placeholder="Paste theme URL here..." id="loadUrlInput" />
<button @click="loadThemeFromUrl">Load Theme</button>
<div class="share-section">
<div>
<h3>What am I looking at?</h3>
<p>Mostly Bash configuration focused on usability. Pick your favorite colors and do a quick install to take advantage of the features below, or read about the <a src="https://">motivation behind the project</a>. Share your theme with others with the generated URL.</p>
<h3>Features:</h3>
</div>
<div>
<ul>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span :class="getColorClassFromBash(generatedColors.SECONDARY_COLOR)">user</span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">@</span>
<span :class="getColorClassFromBash(generatedColors.PRIMARY_COLOR)">myhost:pts/5</span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
</span>
- Username (highlighted if root) and hostname.
</li>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span class="text-yellow"></span>
<span class="text-bright-black"></span>
<span class="text-bright-white"></span>
<span class="text-cyan"></span>
<span class="text-bright-white"></span>
<span class="text-bright-black"></span>
<span class="text-yellow"></span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
</span>
- Unique avatar based on hostname (a bit like automatic avatars on StackOverflow), reduces the risk of terminal confusion.
</li>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span :class="getColorClassFromBash(generatedColors.SECONDARY_COLOR)">1 </span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
</span>
- Number of background processes.
</li>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.BORDCOL)"></span>
</span>
- Line separating commands output.
</li>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span :class="getColorClassFromBash(generatedColors.ERR_COLOR)">127 </span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
</span>
- Exit code if other than zero.
</li>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span :class="getColorClassFromBash(generatedColors.TIME_COLOR)">Wed May 14</span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
<span :class="getColorClassFromBash(generatedColors.BORDCOL)"></span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span :class="getColorClassFromBash(generatedColors.PRIMARY_COLOR)">00:40:12</span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
</span>
- Date and time.
</li>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span :class="getColorClassFromBash(generatedColors.PATH_COLOR)">~/repo</span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
</span>
- Current directory.
</li>
<li>
<span class="terminal-inline">
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">(</span>
<span :class="getColorClassFromBash(generatedColors.PRIMARY_COLOR)">$</span>
<span :class="getColorClassFromBash(generatedColors.RST)"> on </span>
<span class="text-green">master</span>
<span :class="getColorClassFromBash(generatedColors.RST)">=</span>
<span :class="getColorClassFromBash(generatedColors.SEPARATOR_COLOR)">)</span>
</span>
- Git status (if current directory inside git repository)
</li>
<li>
<span class="terminal-inline">
<span>&lt;cmd_prefix&gt; + </span>
</span>
- Rapid search history with up/down keyboard arrows
</li>
</ul>
</div>
<p v-if="loadError" class="load-error-message">{{ loadError }}</p>
<p v-if="loadSuccess" class="load-success-message">Theme loaded successfully!</p>-->
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
// default theme vN-y_5uA
// Color labels for UI
const colorLabels = {
@ -204,6 +342,8 @@ const ENCODING_ORDERED_COLOR_KEYS = [
// Avatar state
const showAvatar = ref(true);
const activeTab = ref('curl');
// --- Base Color Definitions (30-37 range) ---
const baseColorDefinitions = {
30: { name: "Black", hex: "#000000", css: "text-black" },
@ -470,11 +610,22 @@ function parseShareCode(code) {
}
}
const installUrl = computed(() => {
const curlInstallUrl = computed(() => {
const code = generateShareCode(selectedColorAttributes.value, showAvatar.value);
return `curl -sL https://betterbash.cz0.cz/${code}/getbb.sh | bash && . ~/.bashrc`;
});
const wgetInstallUrl = computed(() => {
const code = generateShareCode(selectedColorAttributes.value, showAvatar.value);
return `wget -q -O - https://betterbash.cz0.cz/${code}/getbb.sh | bash && . ~/.bashrc`;
});
const opensslInstallUrl = computed(() => {
const code = generateShareCode(selectedColorAttributes.value, showAvatar.value);
const backend = 'bbbt-bdewcgb9h5h6dfda.westeurope-01.azurewebsites.net'
return `echo -e "GET /${code}/getbb.sh HTTP/1.1\\r\\nHost: ${backend}\\r\\nConnection: close\\r\\n\\r\\n" \\\r\n| openssl s_client -quiet -connect ${backend}:443 2>/dev/null \\\r\n| sed '1,/^\\r$/d' | bash && . ~/.bashrc`;
});
const shareableUrl = computed(() => {
const code = generateShareCode(selectedColorAttributes.value, showAvatar.value);
return `${window.location.origin}${window.location.pathname}#${code}`;
@ -495,9 +646,36 @@ async function copyUrlToClipboard() {
}
const copyCmdSuccess = ref(false);
async function copyCmdToClipboard() {
async function copyCurlCmdToClipboard() {
try {
await navigator.clipboard.writeText(installCurlCmd.value);
copyCmdSuccess.value = true;
setTimeout(() => {
copyCmdSuccess.value = false;
}, 2000);
} catch (err) {
console.error('Failed to copy install command: ', err);
alert('Failed to copy install command. Please copy it manually.');
}
}
async function copyWgetCmdToClipboard() {
try {
await navigator.clipboard.writeText(installWgetCmd.value);
copyCmdSuccess.value = true;
setTimeout(() => {
copyCmdSuccess.value = false;
}, 2000);
} catch (err) {
console.error('Failed to copy install command: ', err);
alert('Failed to copy install command. Please copy it manually.');
}
}
async function copyOpensslCmdToClipboard() {
try {
await navigator.clipboard.writeText(installUrl.value);
await navigator.clipboard.writeText(installOpensslCmd.value);
copyCmdSuccess.value = true;
setTimeout(() => {
copyCmdSuccess.value = false;
@ -597,16 +775,39 @@ body {
background-color: #1e1e1e;
color: #f0f0f0;
}
p {
margin-bottom: 16px;
}
li {
padding: 2px 0px 2px 0px
}
.logo {
float: left;
width: 150px;
image-rendering: pixelated;
margin-right: 15px;
}
.container {
max-width: 1200px;
max-width: 1000px;
/*max-width: 1200px;*/
margin: 0 auto;
background-color: #1e1e1e;
color: #f0f0f0;
}
h1 {
text-align: center;
margin-bottom: 30px;
color: #10b981; /* A pleasant green */
color: #4e9a06; /* A pleasant green */
font-family: "Tiny5", sans-serif;
font-weight: 400;
font-style: normal;
font-size: 64px;
}
a {
color: #4e9a06;
border-bottom: 2px dotted #4e9a06;
}
a:hover {
color: #076519;
}
.customizer {
display: flex;
@ -615,7 +816,7 @@ h1 {
}
.color-controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
}
.color-select-group {
@ -625,30 +826,36 @@ h1 {
padding: 15px;
border-radius: 8px;
font-family: Consolas, Monaco, 'Lucida Console', monospace;
gap: 10px;
gap: 4px;
}
.color-selector-main label {
margin-bottom: 8px;
/*margin-bottom: 8px;*/
font-weight: bold;
display: flex;
/*display: flex;*/
align-items: center;
flex: 1;
flex-basis: 30%;
}
.color-selector-main select {
width: 100%;
padding: 8px;
/*.color-selector-main select {*/
.color-modifier-checkboxes select {
/*width: 100%;*/
padding: 3px;
margin-right: 10px;
background-color: #3a3a3a;
color: #fff;
border: 1px solid #555;
border-radius: 4px;
font-family: Consolas, Monaco, 'Lucida Console', monospace;
flex: 1;
flex-basis: 30%;
}
.color-modifier-checkboxes {
display: flex;
/*display: flex;*/
gap: 15px;
margin-top: 5px;
/*margin-top: 5px;*/
}
.color-modifier-checkboxes label {
display: flex;
/*display: flex;*/
align-items: center;
gap: 5px;
font-weight: normal;
@ -656,14 +863,23 @@ h1 {
.color-modifier-checkboxes input[type="checkbox"] {
margin-right: 4px;
}
.terminal-inline {
background-color: #000;
padding: 4px 6px 4px 8px;
border-radius: 4px;
min-height: 180px;
font-family: Consolas, Monaco, 'Lucida Console', monospace;
white-space: pre-wrap;
line-height: 0.9;
font-size: 18px;
}
.terminal {
background-color: #000;
padding: 20px;
border-radius: 8px;
min-height: 180px;
font-family: Consolas, Monaco, 'Lucida Console', monospace;
overflow-x: auto;
margin-top: 20px;
margin-top: 10px;
white-space: pre-wrap;
line-height: 0.9;
font-size: 18px;
@ -675,19 +891,19 @@ h1 {
display: inline-block;
width: 20px;
height: 20px;
margin-left: 10px;
/*margin-left: 10px*/;
border: 1px solid #555;
vertical-align: middle;
}
.info-section, .share-section {
margin-top: 30px;
margin-top: 10px;
background-color: #2d2d2d;
padding: 20px;
border-radius: 8px;
}
.info-section h3, .share-section h3 {
margin-top: 0;
color: #10b981;
color: #4e9a06;
}
.share-url-container {
display: flex;
@ -703,9 +919,23 @@ h1 {
border-radius: 4px;
font-family: Consolas, Monaco, 'Lucida Console', monospace;
}
.share-url-container textarea {
flex-grow: 1;
padding: 8px;
background-color: #3a3a3a;
color: #f0f0f0;
border: 1px solid #555;
border-radius: 4px;
font-family: Consolas, Monaco, 'Lucida Console', monospace;
resize: vertical;
min-height: 80px;
}
.share-url-container.multiline {
align-items: flex-start;
}
.share-url-container button {
padding: 8px 15px;
background-color: #10b981;
background-color: #4e9a06;
color: #fff;
border: none;
border-radius: 4px;
@ -713,8 +943,41 @@ h1 {
font-weight: bold;
}
.share-url-container button:hover {
background-color: #0da874;
background-color: #076519;
}
/* Tab styles */
.tab-navigation {
display: flex;
margin-bottom: 15px;
border-bottom: 1px solid #555;
}
.tab-button {
padding: 10px 20px;
background-color: transparent;
color: #f0f0f0;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
font-family: Consolas, Monaco, 'Lucida Console', monospace;
font-size: 14px;
transition: all 0.3s ease;
}
.tab-button:hover {
background-color: #3a3a3a;
}
.tab-button.active {
color: #4e9a06;
border-bottom-color: #4e9a06;
font-weight: bold;
}
.tab-content {
margin-top: 15px;
}
.tab-panel {
min-height: 50px;
}
.copy-success-message {
color: #8ae234; /* Bright Green */
font-size: 0.9em;
@ -722,7 +985,7 @@ h1 {
}
.decoding-explanation {
font-size: 0.9em;
line-height: 1.6;
/*line-height: 1.6;*/
}
.decoding-explanation ul {
padding-left: 20px;

@ -1,47 +0,0 @@
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 276 B

@ -1,329 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bash PS1 Prompt Customizer</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.19/vue.global.prod.min.js"></script>
<style>
body {
font-family: 'Courier New', monospace;
margin: 0;
padding: 20px;
background-color: #1e1e1e;
color: #f0f0f0;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 30px;
color: #10b981;
}
.customizer {
display: flex;
flex-direction: column;
gap: 20px;
}
.color-controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 15px;
}
.color-select {
display: flex;
flex-direction: column;
background-color: #2d2d2d;
padding: 15px;
border-radius: 8px;
}
label {
margin-bottom: 8px;
font-weight: bold;
}
select {
padding: 8px;
background-color: #3a3a3a;
color: #fff;
border: 1px solid #555;
border-radius: 4px;
}
.terminal {
background-color: #000;
padding: 20px;
border-radius: 8px;
min-height: 180px;
font-family: 'Courier New', monospace;
overflow-x: auto;
margin-top: 20px;
white-space: pre-wrap;
}
.ps1-line {
margin-bottom: 5px;
}
.ps1-input {
margin-top: 5px;
}
.color-preview {
display: inline-block;
width: 20px;
height: 20px;
margin-left: 10px;
border: 1px solid #555;
vertical-align: middle;
}
.info-section {
margin-top: 30px;
background-color: #2d2d2d;
padding: 15px;
border-radius: 8px;
}
/* Terminal text colors */
.text-black { color: #000000; }
.text-red { color: #cc0000; }
.text-green { color: #4e9a06; }
.text-yellow { color: #c4a000; }
.text-blue { color: #3465a4; }
.text-magenta { color: #75507b; }
.text-cyan { color: #06989a; }
.text-white { color: #d3d7cf; }
.text-bright-black { color: #555753; }
.text-bright-red { color: #ef2929; }
.text-bright-green { color: #8ae234; }
.text-bright-yellow { color: #fce94f; }
.text-bright-blue { color: #729fcf; }
.text-bright-magenta { color: #ad7fa8; }
.text-bright-cyan { color: #34e2e2; }
.text-bright-white { color: #eeeeec; }
.text-bold-red { color: #ff4d4d; font-weight: bold; }
.text-bold-green { color: #73d216; font-weight: bold; }
.text-bold-yellow { color: #edd400; font-weight: bold; }
.text-bold-blue { color: #3584e4; font-weight: bold; }
.text-bold-magenta { color: #ad7fa8; font-weight: bold; }
.text-bold-cyan { color: #34e2e2; font-weight: bold; }
.text-bold-white { color: #ffffff; font-weight: bold; }
.text-bold-bright-magenta { color: #d070d0; font-weight: bold; }
.text-bold-bright-black { color: #7c7c7c; font-weight: bold; }
.text-bold-bright-white { color: #ffffff; font-weight: bold; }
.text-red-on-white { color: #ffffff; background-color: #cc0000; font-weight: bold; }
.text-reset { color: inherit; }
</style>
</head>
<body>
<div id="app" class="container">
<h1>Bash PS1 Prompt Customizer</h1>
<div class="customizer">
<div class="color-controls">
<div class="color-select" v-for="(colorCode, colorName) in colors" :key="colorName">
<label>
{{ colorLabels[colorName] || colorName }}
<div class="color-preview" :style="{ backgroundColor: getPreviewColor(colorCode) }"></div>
</label>
<select v-model="colors[colorName]" @change="updatePrompt">
<option v-for="(color, name) in terminalColors" :key="name" :value="color.code">
{{ name }} ({{ color.code }})
</option>
</select>
</div>
</div>
<div class="terminal">
<div class="ps1-line">
<span class="text-bold-bright-black"></span><span class="text-bold-bright-white">(</span><span class="text-bold-bright-magenta">user</span><span class="text-bold-bright-white">@</span><span class="text-bright-green">hostname</span><span class="text-bold-bright-white">:pts/5)</span><span class="text-bold-bright-black"></span><span class="text-bold-bright-white">(</span><span class="text-bright-green"></span><span class="text-bold-bright-white">)</span><span class="text-bold-bright-black"></span><span class="text-bold-bright-white">(</span><span class="text-bright-yellow">Wed May 14</span><span class="text-bold-bright-white">)</span><span class="text-bold-bright-black"></span><span class="text-bold-bright-white">(</span><span class="text-bright-green">00:40:00</span><span class="text-bold-bright-white">)</span><span class="text-bold-bright-black"></span>
</div>
<div class="ps1-line">
<span class="text-bold-bright-black"></span><span class="text-bold-bright-white">(</span><span class="text-bold-bright-white">~</span><span class="text-bold-bright-white">)</span><span class="text-bold-bright-black"></span><span class="text-bold-bright-white">(</span><span class="text-bright-green">$</span><span class="text-bold-bright-white">)</span><span class="text-bold-bright-black">-></span>
</div>
<div class="ps1-input">$ _</div>
</div>
<div class="info-section">
<h3>About This Customizer</h3>
<p>This tool allows you to customize the colors of your bash PS1 prompt. Select different terminal colors for each component to see how they look in the preview.</p>
<p>To use your customized prompt, you'll need to replace the color variables in your .bashrc file with the selected values.</p>
</div>
</div>
</div>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
// Terminal colors mapping
const terminalColors = {
'Black': { code: '\\[\\033[00;30m\\]', hex: '#000000', cssClass: 'text-black' },
'Red': { code: '\\[\\033[00;31m\\]', hex: '#cc0000', cssClass: 'text-red' },
'Green': { code: '\\[\\033[00;32m\\]', hex: '#4e9a06', cssClass: 'text-green' },
'Yellow': { code: '\\[\\033[00;33m\\]', hex: '#c4a000', cssClass: 'text-yellow' },
'Blue': { code: '\\[\\033[00;34m\\]', hex: '#3465a4', cssClass: 'text-blue' },
'Magenta': { code: '\\[\\033[00;35m\\]', hex: '#75507b', cssClass: 'text-magenta' },
'Cyan': { code: '\\[\\033[00;36m\\]', hex: '#06989a', cssClass: 'text-cyan' },
'White': { code: '\\[\\033[00;37m\\]', hex: '#d3d7cf', cssClass: 'text-white' },
'Bright Black': { code: '\\[\\033[00;90m\\]', hex: '#555753', cssClass: 'text-bright-black' },
'Bright Red': { code: '\\[\\033[00;91m\\]', hex: '#ef2929', cssClass: 'text-bright-red' },
'Bright Green': { code: '\\[\\033[00;92m\\]', hex: '#8ae234', cssClass: 'text-bright-green' },
'Bright Yellow': { code: '\\[\\033[00;93m\\]', hex: '#fce94f', cssClass: 'text-bright-yellow' },
'Bright Blue': { code: '\\[\\033[00;94m\\]', hex: '#729fcf', cssClass: 'text-bright-blue' },
'Bright Magenta': { code: '\\[\\033[00;95m\\]', hex: '#ad7fa8', cssClass: 'text-bright-magenta' },
'Bright Cyan': { code: '\\[\\033[00;96m\\]', hex: '#34e2e2', cssClass: 'text-bright-cyan' },
'Bright White': { code: '\\[\\033[00;97m\\]', hex: '#eeeeec', cssClass: 'text-bright-white' },
'Bold Red': { code: '\\[\\033[00;31;1m\\]', hex: '#ff4d4d', cssClass: 'text-bold-red' },
'Bold Green': { code: '\\[\\033[00;32;1m\\]', hex: '#73d216', cssClass: 'text-bold-green' },
'Bold Yellow': { code: '\\[\\033[00;33;1m\\]', hex: '#edd400', cssClass: 'text-bold-yellow' },
'Bold Blue': { code: '\\[\\033[00;34;1m\\]', hex: '#3584e4', cssClass: 'text-bold-blue' },
'Bold Magenta': { code: '\\[\\033[00;35;1m\\]', hex: '#ad7fa8', cssClass: 'text-bold-magenta' },
'Bold Cyan': { code: '\\[\\033[00;36;1m\\]', hex: '#34e2e2', cssClass: 'text-bold-cyan' },
'Bold White': { code: '\\[\\033[00;37;1m\\]', hex: '#ffffff', cssClass: 'text-bold-white' },
'Bold Bright Magenta': { code: '\\[\\033[00;95;1m\\]', hex: '#d070d0', cssClass: 'text-bold-bright-magenta' },
'Bold Bright Black': { code: '\\[\\033[00;90;1m\\]', hex: '#7c7c7c', cssClass: 'text-bold-bright-black' },
'Bold Bright White': { code: '\\[\\033[00;97;1m\\]', hex: '#ffffff', cssClass: 'text-bold-bright-white' },
'Red on White': { code: '\\033[00;41;97;1m', hex: '#ff0000', cssClass: 'text-red-on-white' },
'Reset': { code: '\\[\\033[0m\\]', hex: '#ffffff', cssClass: 'text-reset' }
};
// Color labels for better readability
const colorLabels = {
'PRIMARY_COLOR': 'Primary Color',
'SECONDARY_COLOR': 'Secondary Color',
'ROOT_COLOR': 'Root Color',
'TIME_COLOR': 'Time Color',
'ERR_COLOR': 'Error Color',
'WHITEB': 'White Bold',
'RST': 'Reset',
'BOLD': 'Bold',
'BORDCOL': 'Border Color',
'PATH_COLOR': 'Path Color'
};
// Default colors from the script
const colors = ref({
'PRIMARY_COLOR': '\\[\\033[00;92m\\]',
'SECONDARY_COLOR': '\\[\\033[00;95;1m\\]',
'ROOT_COLOR': '\\033[00;41;97;1m',
'TIME_COLOR': '\\[\\033[00;93;1m\\]',
'ERR_COLOR': '\\[\\033[00;31;1m\\]',
'WHITEB': '\\[\\033[00;97;1m\\]',
'RST': '\\[\\033[0m\\]',
'BOLD': '\\[\\033[1m\\]',
'BORDCOL': '\\[\\033[00;90;1m\\]',
'PATH_COLOR': '\\[\\033[00;97;1m\\]'
});
// Get the hex color for preview
const getPreviewColor = (colorCode) => {
for (const [name, color] of Object.entries(terminalColors)) {
if (color.code === colorCode) {
return color.hex;
}
}
return '#ffffff';
};
// Update the prompt preview when colors change
const updatePrompt = () => {
console.log("Updating prompt colors");
// Get the elements we need to update
const firstLine = document.querySelector('.terminal .ps1-line:first-child');
const secondLine = document.querySelector('.terminal .ps1-line:nth-child(2)');
if (firstLine && secondLine) {
// Update the first line colors
const firstLineSpans = firstLine.querySelectorAll('span');
if (firstLineSpans.length >= 10) {
// Border color
firstLineSpans[0].className = getColorClass(colors.value.BORDCOL);
// White bold
firstLineSpans[1].className = getColorClass(colors.value.WHITEB);
// Secondary color (user)
firstLineSpans[2].className = getColorClass(colors.value.SECONDARY_COLOR);
// White bold
firstLineSpans[3].className = getColorClass(colors.value.WHITEB);
// Primary color (hostname)
firstLineSpans[4].className = getColorClass(colors.value.PRIMARY_COLOR);
// White bold
firstLineSpans[5].className = getColorClass(colors.value.WHITEB);
// Border color
firstLineSpans[6].className = getColorClass(colors.value.BORDCOL);
// White bold
firstLineSpans[7].className = getColorClass(colors.value.WHITEB);
// Primary color (symbols)
firstLineSpans[8].className = getColorClass(colors.value.PRIMARY_COLOR);
// White bold
firstLineSpans[9].className = getColorClass(colors.value.WHITEB);
// Border color
firstLineSpans[10].className = getColorClass(colors.value.BORDCOL);
// White bold
firstLineSpans[11].className = getColorClass(colors.value.WHITEB);
// Time color
firstLineSpans[12].className = getColorClass(colors.value.TIME_COLOR);
// White bold
firstLineSpans[13].className = getColorClass(colors.value.WHITEB);
// Border color
firstLineSpans[14].className = getColorClass(colors.value.BORDCOL);
// White bold
firstLineSpans[15].className = getColorClass(colors.value.WHITEB);
// Primary color (time)
firstLineSpans[16].className = getColorClass(colors.value.PRIMARY_COLOR);
// White bold
firstLineSpans[17].className = getColorClass(colors.value.WHITEB);
// Border color
firstLineSpans[18].className = getColorClass(colors.value.BORDCOL);
}
// Update the second line colors
const secondLineSpans = secondLine.querySelectorAll('span');
if (secondLineSpans.length >= 7) {
// Border color
secondLineSpans[0].className = getColorClass(colors.value.BORDCOL);
// White bold
secondLineSpans[1].className = getColorClass(colors.value.WHITEB);
// Path color
secondLineSpans[2].className = getColorClass(colors.value.PATH_COLOR);
// White bold
secondLineSpans[3].className = getColorClass(colors.value.WHITEB);
// Border color
secondLineSpans[4].className = getColorClass(colors.value.BORDCOL);
// White bold
secondLineSpans[5].className = getColorClass(colors.value.WHITEB);
// Primary color ($)
secondLineSpans[6].className = getColorClass(colors.value.PRIMARY_COLOR);
// White bold
secondLineSpans[7].className = getColorClass(colors.value.WHITEB);
// Border color
secondLineSpans[8].className = getColorClass(colors.value.BORDCOL);
}
}
};
// Get CSS class for a color code
const getColorClass = (colorCode) => {
for (const [name, color] of Object.entries(terminalColors)) {
if (color.code === colorCode) {
return color.cssClass;
}
}
return 'text-reset';
};
return {
colors,
terminalColors,
colorLabels,
getPreviewColor,
updatePrompt
};
},
mounted() {
// Initial update
this.updatePrompt();
}
}).mount('#app');
</script>
</body>
</html>
Loading…
Cancel
Save