mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-25 09:37:37 +00:00
transfer the argument suggestion into popup, show available commands, highlight currently typing argument
This commit is contained in:
parent
5b229c2dcd
commit
fc7455b914
@ -27,10 +27,8 @@ from mitmproxy.utils.strutils import always_str
|
|||||||
def flow_to_json(flow: mitmproxy.flow.Flow) -> dict:
|
def flow_to_json(flow: mitmproxy.flow.Flow) -> dict:
|
||||||
"""
|
"""
|
||||||
Remove flow message content and cert to save transmission space.
|
Remove flow message content and cert to save transmission space.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
flow: The original flow.
|
flow: The original flow.
|
||||||
|
|
||||||
Sync with web/src/flow.ts.
|
Sync with web/src/flow.ts.
|
||||||
"""
|
"""
|
||||||
f = {
|
f = {
|
||||||
@ -454,12 +452,20 @@ class FlowContentView(RequestHandler):
|
|||||||
|
|
||||||
|
|
||||||
class Commands(RequestHandler):
|
class Commands(RequestHandler):
|
||||||
|
def get(self):
|
||||||
|
commands = {}
|
||||||
|
for (name, command) in self.master.commands.commands.items():
|
||||||
|
commands[name] = []
|
||||||
|
for parameter in command.parameters:
|
||||||
|
commands[name].append({"name": parameter.name})
|
||||||
|
self.write({"commands": commands})
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
result = self.master.commands.execute(self.json["command"])
|
result = self.master.commands.execute(self.json["command"])
|
||||||
if result is None:
|
if result is None:
|
||||||
self.write({"result": ""})
|
self.write({"result": ""})
|
||||||
return
|
return
|
||||||
self.write({"result": str(result)})
|
self.write({ "result": result, "type": type(result).__name__ })
|
||||||
|
|
||||||
|
|
||||||
class Events(RequestHandler):
|
class Events(RequestHandler):
|
||||||
@ -479,16 +485,6 @@ class Options(RequestHandler):
|
|||||||
raise APIError(400, f"{err}")
|
raise APIError(400, f"{err}")
|
||||||
|
|
||||||
|
|
||||||
class CommandArguments(RequestHandler):
|
|
||||||
def get(self):
|
|
||||||
arguments = {}
|
|
||||||
for (name, command) in self.master.commands.commands.items():
|
|
||||||
arguments[name] = []
|
|
||||||
for parameter in command.parameters:
|
|
||||||
arguments[name].append(parameter.name)
|
|
||||||
self.write(arguments)
|
|
||||||
|
|
||||||
|
|
||||||
class SaveOptions(RequestHandler):
|
class SaveOptions(RequestHandler):
|
||||||
def post(self):
|
def post(self):
|
||||||
# try:
|
# try:
|
||||||
@ -541,7 +537,7 @@ class Application(tornado.web.Application):
|
|||||||
(r"/", IndexHandler),
|
(r"/", IndexHandler),
|
||||||
(r"/filter-help(?:\.json)?", FilterHelp),
|
(r"/filter-help(?:\.json)?", FilterHelp),
|
||||||
(r"/updates", ClientConnection),
|
(r"/updates", ClientConnection),
|
||||||
(r"/commands", Commands),
|
(r"/commands(?:\.json)?", Commands),
|
||||||
(r"/events(?:\.json)?", Events),
|
(r"/events(?:\.json)?", Events),
|
||||||
(r"/flows(?:\.json)?", Flows),
|
(r"/flows(?:\.json)?", Flows),
|
||||||
(r"/flows/dump", DumpFlows),
|
(r"/flows/dump", DumpFlows),
|
||||||
@ -559,7 +555,6 @@ class Application(tornado.web.Application):
|
|||||||
r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response)/content/(?P<content_view>[0-9a-zA-Z\-\_]+)(?:\.json)?",
|
r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response)/content/(?P<content_view>[0-9a-zA-Z\-\_]+)(?:\.json)?",
|
||||||
FlowContentView),
|
FlowContentView),
|
||||||
(r"/clear", ClearAll),
|
(r"/clear", ClearAll),
|
||||||
(r"/arguments(?:\.json)?", CommandArguments),
|
|
||||||
(r"/options(?:\.json)?", Options),
|
(r"/options(?:\.json)?", Options),
|
||||||
(r"/options/save", SaveOptions),
|
(r"/options/save", SaveOptions),
|
||||||
(r"/conf\.js", Conf),
|
(r"/conf\.js", Conf),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.command {
|
.command-title {
|
||||||
background-color: #F2F2F2;
|
background-color: #F2F2F2;
|
||||||
border: 1px solid #aaa;
|
border: 1px solid #aaa;
|
||||||
}
|
}
|
||||||
@ -14,4 +14,18 @@
|
|||||||
|
|
||||||
.command-suggestion {
|
.command-suggestion {
|
||||||
background-color: #9c9c9c;
|
background-color: #9c9c9c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.argument-suggestion {
|
||||||
|
background-color: hsla(209, 52%, 84%, 0.5) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command > .popover {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-commands {
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
@ -1,13 +1,10 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import renderer from 'react-test-renderer'
|
|
||||||
import CommandBar from '../../../components/CommandBar'
|
import CommandBar from '../../../components/CommandBar'
|
||||||
|
import { render } from "../../test-utils"
|
||||||
|
|
||||||
describe('CommandBar Component', () => {
|
test('CommandBar Component', async () => {
|
||||||
let commandBar = renderer.create(
|
const {asFragment, store} = render(
|
||||||
<CommandBar />),
|
<CommandBar/>
|
||||||
tree = commandBar.toJSON()
|
);
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
it('should render correctly', () => {
|
|
||||||
expect(tree).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
})
|
@ -3,39 +3,76 @@ import classnames from 'classnames'
|
|||||||
import { Key, fetchApi } from '../utils'
|
import { Key, fetchApi } from '../utils'
|
||||||
import Filt from '../filt/command'
|
import Filt from '../filt/command'
|
||||||
|
|
||||||
|
export function AvailableCommands({input, commands}) {
|
||||||
|
if (!commands) return null
|
||||||
|
let availableCommands = []
|
||||||
|
for (const [command, args] of Object.entries(commands)) {
|
||||||
|
if (command.startsWith(input)) {
|
||||||
|
availableCommands.push(command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <div className="available-commands popover bottom">Available Commands: {JSON.stringify(availableCommands)}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArgumentSuggestion({nextArgs, currentArg}){
|
||||||
|
let results = []
|
||||||
|
for (let i = 0; i < nextArgs.length; i++) {
|
||||||
|
if (i==currentArg) {
|
||||||
|
results.push(<mark>{nextArgs[i]}</mark>)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results.push(<span>{nextArgs[i]} </span>)
|
||||||
|
}
|
||||||
|
return (<div className="argument-suggestion popover top">
|
||||||
|
<div className="arrow"/>
|
||||||
|
<div className="popover-content">
|
||||||
|
Argument suggestion: {results}
|
||||||
|
</div>
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
|
||||||
export default function CommandBar() {
|
export default function CommandBar() {
|
||||||
|
const [input, setInput] = useState("")
|
||||||
const [command, setCommand] = useState("")
|
const [command, setCommand] = useState("")
|
||||||
const [results, setResults] = useState([])
|
const [results, setResults] = useState([])
|
||||||
const [history, setHistory] = useState([])
|
const [history, setHistory] = useState([])
|
||||||
const [currentPos, setCurrentPos] = useState(0)
|
const [currentPos, setCurrentPos] = useState(0)
|
||||||
const [args, setArgs] = useState({})
|
const [allCommands, setAllCommands] = useState({})
|
||||||
const [nextArgs, setNextArgs] = useState([])
|
const [nextArgs, setNextArgs] = useState([])
|
||||||
|
const [currentArg, setCurrentArg] = useState(0)
|
||||||
|
const [commandHelp, setCommandHelp] = useState("")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchApi('/arguments')
|
fetchApi('/commands', { method: 'GET' })
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => setArgs(data))
|
.then(data => setAllCommands(data))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const parseCommand = (input) => {
|
const parseCommand = (input) => {
|
||||||
const parts = Filt.parse(input)
|
const parts = Filt.parse(input)
|
||||||
|
if (allCommands["commands"].hasOwnProperty(parts[0])){
|
||||||
|
setCommand(parts[0])
|
||||||
|
} else {
|
||||||
|
setCommand("")
|
||||||
|
}
|
||||||
|
|
||||||
const nextArgs = args[parts[0]]
|
const nextArgs = allCommands["commands"][parts[0]]?.map(arg => arg.name)
|
||||||
|
|
||||||
if (nextArgs) {
|
if (nextArgs) {
|
||||||
setNextArgs([parts[0], ...nextArgs])
|
setNextArgs([parts[0], ...nextArgs])
|
||||||
|
setCurrentArg(parts.length-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChange = (e) => {
|
const onChange = (e) => {
|
||||||
setCommand(e.target.value)
|
setInput(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onKeyDown = (e) => {
|
const onKeyDown = (e) => {
|
||||||
if (e.keyCode === Key.ENTER) {
|
if (e.keyCode === Key.ENTER) {
|
||||||
const body = {"command": command}
|
const body = {"command": input}
|
||||||
const newHistory = Object.assign([], history)
|
const newHistory = Object.assign([], history)
|
||||||
newHistory.splice(currentPos, 0, command)
|
newHistory.splice(currentPos, 0, input)
|
||||||
|
|
||||||
fetchApi(`/commands`, {
|
fetchApi(`/commands`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -48,21 +85,21 @@ export default function CommandBar() {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
setHistory(newHistory)
|
setHistory(newHistory)
|
||||||
setCurrentPos(currentPos + 1)
|
setCurrentPos(currentPos + 1)
|
||||||
|
setNextArgs([])
|
||||||
|
|
||||||
if (data.result == "") return
|
|
||||||
setResults([...results, {"id": results.length, "result": data.result}])
|
setResults([...results, {"id": results.length, "result": data.result}])
|
||||||
})
|
})
|
||||||
|
|
||||||
setCommand("")
|
setInput("")
|
||||||
}
|
}
|
||||||
if (e.keyCode === Key.UP) {
|
if (e.keyCode === Key.UP) {
|
||||||
if (currentPos > 0) {
|
if (currentPos > 0) {
|
||||||
setCommand(history[currentPos - 1])
|
setInput(history[currentPos - 1])
|
||||||
setCurrentPos(currentPos - 1)
|
setCurrentPos(currentPos - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e.keyCode === Key.DOWN) {
|
if (e.keyCode === Key.DOWN) {
|
||||||
setCommand(history[currentPos])
|
setInput(history[currentPos])
|
||||||
if (currentPos < history.length -1) {
|
if (currentPos < history.length -1) {
|
||||||
setCurrentPos(currentPos + 1)
|
setCurrentPos(currentPos + 1)
|
||||||
}
|
}
|
||||||
@ -71,14 +108,14 @@ export default function CommandBar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onKeyUp = (e) => {
|
const onKeyUp = (e) => {
|
||||||
if (command == "") return
|
if (input == "") return
|
||||||
parseCommand(command)
|
parseCommand(input)
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="command">
|
||||||
<div className="command">
|
<div className="command-title">
|
||||||
Command Result
|
Command Result
|
||||||
</div>
|
</div>
|
||||||
<div className="command-result">
|
<div className="command-result">
|
||||||
@ -88,7 +125,7 @@ export default function CommandBar() {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{ nextArgs ? <div className="command-suggestion">Argument suggestion: {nextArgs.join(" ")}</div> : null }
|
{ nextArgs.length > 0 && <ArgumentSuggestion nextArgs={nextArgs} currentArg={currentArg} /> }
|
||||||
<div className={classnames('command-input input-group')}>
|
<div className={classnames('command-input input-group')}>
|
||||||
<span className="input-group-addon">
|
<span className="input-group-addon">
|
||||||
<i className={'fa fa-fw fa-terminal'}/>
|
<i className={'fa fa-fw fa-terminal'}/>
|
||||||
@ -97,12 +134,13 @@ export default function CommandBar() {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter command"
|
placeholder="Enter command"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
value={command}
|
value={input}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
onKeyUp={onKeyUp}
|
onKeyUp={onKeyUp}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
{ !command && <AvailableCommands input={input} commands={allCommands["commands"]} /> }
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user