Skip to content

Commit

Permalink
[feat] Add the command usage syntax hint, inline
Browse files Browse the repository at this point in the history
  • Loading branch information
shubhdevelop committed Oct 5, 2024
1 parent 69e32bc commit 57166dc
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 5 deletions.
32 changes: 28 additions & 4 deletions src/components/CLI/CLI.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
// src/components/CLI/CLI.tsx
'use client';

import React from 'react';
// hooks
import { useCli } from './hooks/useCli';
import { SyntaxPart } from '@/data/commandSyntaxMap';

interface CliProps {
decreaseCommandsLeft: () => void;
}

const InlineHint = ({ part }: { part: SyntaxPart }) => (
<span className="border-b border-dotted border-gray-600">
{" " + part.syntax}
</span>
);

export default function Cli({ decreaseCommandsLeft }: CliProps) {
const {
handleInputChange,
Expand All @@ -16,7 +24,10 @@ export default function Cli({ decreaseCommandsLeft }: CliProps) {
inputRef,
output,
command,
remainingSyntax
} = useCli(decreaseCommandsLeft);


return (
<div
ref={terminalRef}
Expand All @@ -29,18 +40,31 @@ export default function Cli({ decreaseCommandsLeft }: CliProps) {
</div>
))}
<div className="flex items-center">
<p className="text-green-500 mr-2 p-1">dice ~$</p>
<div className="flex-grow">
<span className="text-green-500 mr-2 p-1 flex-shrink-0">dice ~$</span>
<div className="flex-grow relative">
<input
ref={inputRef}
type="text"
value={command}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
className="w-full bg-transparent outline-none text-white"
className="w-full bg-transparent outline-none text-white "
/>
<div className="absolute top-0 left-0 text-gray-500 pointer-events-none whitespace-wrap overflow-x-auto"
style={{ paddingLeft: `${command.length + 1}ch ` }}>
{remainingSyntax.map((part, index) => (
<React.Fragment key={index}>
{index > 0 && ' '}
<InlineHint part={part} />
</React.Fragment>
))}
</div>
</div>
</div>
</div>
</div >
);
}




33 changes: 32 additions & 1 deletion src/components/CLI/hooks/useCli.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// react
import { useState, useEffect, useRef, KeyboardEvent, ChangeEvent } from 'react';

// utils
import { handleCommand } from '@/shared/utils/cliUtils';
import blacklistedCommands from '@/shared/utils/blacklist'; // Assuming you added blacklist here
import { syntaxMap } from '@/data/commandSyntaxMap';

export const useCli = (decreaseCommandsLeft: () => void) => {
// states
Expand All @@ -13,11 +15,35 @@ export const useCli = (decreaseCommandsLeft: () => void) => {
//Initialise the command history with sessionStorage
const [commandHistory, setCommandHistory] = useState<string[]>([]);
const [historyIndex, setHistoryIndex] = useState<number>(-1);
const [remainingSyntax, setRemainingSyntax] = useState<SyntaxPart[]>([]);

// useRefs
const terminalRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);





const updateSyntax = (value: string) => {
const inputParts = value.trim().split(' ');
const command = inputParts[0].toUpperCase();

if (syntaxMap[command]) {
const parts = syntaxMap[command].parts;
if (inputParts.length === 1) {
// Only command typed, show all parts
setRemainingSyntax(parts);
} else {
// Show remaining parts based on what's already typed
const remainingParts = parts.slice(inputParts.length - 1);
setRemainingSyntax(remainingParts);
}
} else {
setRemainingSyntax([]);
}
};

const handleCommandWrapper = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
const commandName = command.trim().split(' ')[0].toUpperCase(); // Extract the command
Expand All @@ -33,6 +59,7 @@ export const useCli = (decreaseCommandsLeft: () => void) => {

setCommand(''); // Clear input
decreaseCommandsLeft(); // Call to update remaining commands

}
};

Expand Down Expand Up @@ -61,7 +88,9 @@ export const useCli = (decreaseCommandsLeft: () => void) => {
}, []);

const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setCommand(e.target.value);
const value = e.target.value;
setCommand(value);
updateSyntax(value);
};

const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
Expand All @@ -71,6 +100,7 @@ export const useCli = (decreaseCommandsLeft: () => void) => {
setCommandHistory((prev) => [...prev, command]);
setHistoryIndex(-1);
}
setRemainingSyntax([])
}

if (e.key === 'ArrowUp') {
Expand Down Expand Up @@ -109,5 +139,6 @@ export const useCli = (decreaseCommandsLeft: () => void) => {
command,
tempCommand,
setTempCommand,
remainingSyntax
};
};
56 changes: 56 additions & 0 deletions src/data/commandSyntaxMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export type SyntaxPart = {
syntax: string;
doc: string;
}

export type CommandSyntax = {
parts: SyntaxPart[];
}

export type SyntaxMap = {
[command: string]: CommandSyntax;
}

export const syntaxMap: SyntaxMap = {
SET: {
parts: [
{
syntax: 'key',
doc: 'The key under which to store the value'
},
{
syntax: 'value',
doc: 'The value to be stored'
},
{
syntax: '[NX | XX]',
doc: 'NX - Only set if key does not exist. XX - Only set if key exists'
},
{
syntax: '[EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]',
doc: 'Options to set the key expiration: EX (seconds), PX (milliseconds), EXAT/PXAT (unix timestamp), or KEEPTTL to retain existing TTL'
}
]
},
GET:{
parts: [
{
syntax:"Key",
doc:"Key of the value you want to retrive"
}
]
},
DEL:{
parts: [
{
syntax:"Key",
doc:"Key that you want to delete"
},{
syntax:"[Key ...]",
doc:"Multiple keys you want to delete"
}
]
},

};

0 comments on commit 57166dc

Please sign in to comment.