Cleanup tabby component (#14)

* Rename tabby to monaco

* Switch to javascript

* Rename st_monaco
add-more-languages
Meng Zhang 2023-03-26 19:47:41 +08:00 committed by GitHub
parent d76ed403c3
commit 562b8d9e7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 9587 additions and 12052 deletions

View File

@ -1,5 +1,5 @@
import streamlit as st import streamlit as st
from components import tabby from components import monaco
from utils.service_info import ServiceInfo from utils.service_info import ServiceInfo
SERVICES = [ SERVICES = [
@ -18,4 +18,4 @@ st.set_page_config(page_title="Tabby Admin")
st.markdown("## Tabby") st.markdown("## Tabby")
st.markdown(" ".join(map(make_badge_markdown, SERVICES))) st.markdown(" ".join(map(make_badge_markdown, SERVICES)))
tabby.editor() monaco.st_monaco()

View File

@ -2,7 +2,7 @@ import os
import streamlit.components.v1 as components import streamlit.components.v1 as components
_RELEASE = __name__ != "__main_" _RELEASE = __name__ != "__main__"
# Declare a Streamlit component. `declare_component` returns a function # Declare a Streamlit component. `declare_component` returns a function
if not _RELEASE: if not _RELEASE:
@ -10,7 +10,7 @@ if not _RELEASE:
# We give the component a simple, descriptive name ("my_component" # We give the component a simple, descriptive name ("my_component"
# does not fit this bill, so please choose something better for your # does not fit this bill, so please choose something better for your
# own component :) # own component :)
"tabby-editor", "streamlit-monaco",
# Pass `url` here to tell Streamlit that the component will be served # Pass `url` here to tell Streamlit that the component will be served
# by the local dev server that you run via `npm run start`. # by the local dev server that you run via `npm run start`.
# (This is useful while your component is in development.) # (This is useful while your component is in development.)
@ -22,7 +22,7 @@ else:
# build directory: # build directory:
parent_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(os.path.abspath(__file__))
build_dir = os.path.join(parent_dir, "frontend/build") build_dir = os.path.join(parent_dir, "frontend/build")
_editor_func = components.declare_component("tabby-editor", path=build_dir) _editor_func = components.declare_component("streamlit-monaco", path=build_dir)
# Create a wrapper function for the component. This is an optional # Create a wrapper function for the component. This is an optional
@ -30,7 +30,7 @@ else:
# `declare_component` and call it done. The wrapper allows us to customize # `declare_component` and call it done. The wrapper allows us to customize
# our component's API: we can pre-process its input args, post-process its # our component's API: we can pre-process its input args, post-process its
# output value, and add a docstring for users. # output value, and add a docstring for users.
def editor(tabby_server_url="http://localhost:5000", key=None): def st_monaco(tabby_server_url="http://localhost:5000", key=None):
_editor_func(tabby_server_url=tabby_server_url, key=key) _editor_func(tabby_server_url=tabby_server_url, key=key)
@ -41,4 +41,4 @@ if not _RELEASE:
import streamlit as st import streamlit as st
tabby_server_url = st.text_input("Tabby Server URL", value="http://localhost:5000") tabby_server_url = st.text_input("Tabby Server URL", value="http://localhost:5000")
tabby(tabby_server_url) st_monaco(tabby_server_url)

View File

@ -0,0 +1,9 @@
{
"files": {
"main.js": "./static/js/main.c86c579e.js",
"index.html": "./index.html"
},
"entrypoints": [
"static/js/main.c86c579e.js"
]
}

View File

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><title>Streamlit Component</title><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Streamlit Component"/><link rel="stylesheet" href="bootstrap.min.css"/><script defer="defer" src="./static/js/main.c86c579e.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

View File

@ -4,15 +4,6 @@ object-assign
@license MIT @license MIT
*/ */
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <http://feross.org>
* @license MIT
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/** /**
@ -32,7 +23,38 @@ object-assign
* limitations under the License. * limitations under the License.
*/ */
/** @license React v0.19.1 /**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js * scheduler.production.min.js
* *
* Copyright (c) Facebook, Inc. and its affiliates. * Copyright (c) Facebook, Inc. and its affiliates.
@ -50,24 +72,6 @@ object-assign
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
/** @license React v16.14.0
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.14.0
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.14.0 /** @license React v16.14.0
* react.production.min.js * react.production.min.js
* *

View File

@ -0,0 +1,16 @@
module.exports = {
webpack: {
configure: {
module: {
rules: [
{
test: /\.m?js$/,
resolve: {
fullySpecified: false,
},
},
],
},
},
},
}

View File

@ -1,29 +1,22 @@
{ {
"name": "streamlit_component_template", "name": "streamlit-monaco",
"version": "1.0.3", "version": "1.0.3",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@monaco-editor/react": "^4.4.6", "@monaco-editor/react": "^4.4.6",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@types/jest": "^24.0.0",
"@types/node": "^12.0.0",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"axios": "^1.3.4", "axios": "^1.3.4",
"react": "^16.13.1", "react": "^18.2.0",
"react-dom": "^16.13.1", "react-dom": "^18.2.0",
"react-scripts": "3.4.1", "react-scripts": "^5.0.1",
"streamlit-component-lib-react-hooks": "^1.0.3" "streamlit-component-lib-react-hooks": "^1.0.3"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^4.6.2" "@craco/craco": "^7.1.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "craco start",
"build": "react-scripts build", "build": "GENERATE_SOURCEMAP=false craco build",
"test": "react-scripts test", "test": "craco test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"eslintConfig": { "eslintConfig": {

View File

@ -2,17 +2,19 @@ import axios from "axios"
import { useRenderData } from "streamlit-component-lib-react-hooks" import { useRenderData } from "streamlit-component-lib-react-hooks"
import React, { useEffect } from "react" import React, { useEffect } from "react"
import Monaco, { useMonaco } from "@monaco-editor/react" import Editor, { useMonaco } from "@monaco-editor/react"
const PythonParseJSON = `def parse_json_lines(filename: str) -> List[Any]: const PythonParseJSON = `def parse_json_lines(filename: str) -> List[Any]:
output = [] output = []
with open(filename, "r", encoding="utf-8") as f: with open(filename, "r", encoding="utf-8") as f:
` `
export default function TabbyEditor() { let TabbyServerURL = "http://localhost:5000"
export default function MonacoEditor() {
const renderData = useRenderData() const renderData = useRenderData()
;(window as any).tabbyServerURL = renderData.args.tabby_server_url TabbyServerURL = renderData.args.tabby_server_url
const monaco = useMonaco() const monaco = useMonaco()
useEffect(() => { useEffect(() => {
@ -25,7 +27,7 @@ export default function TabbyEditor() {
return ( return (
<div style={{ height: 400 }}> <div style={{ height: 400 }}>
<Monaco <Editor
theme="vs-dark" theme="vs-dark"
defaultLanguage="python" defaultLanguage="python"
defaultValue={PythonParseJSON} defaultValue={PythonParseJSON}
@ -35,21 +37,12 @@ export default function TabbyEditor() {
} }
class CompletionProvider { class CompletionProvider {
private monaco: any constructor(monaco: Monaco) {
private latestTimestamp: number
private pendingRequest: any
constructor(monaco: any) {
this.monaco = monaco this.monaco = monaco
this.latestTimestamp = 0 this.latestTimestamp = 0
} }
async provideInlineCompletions( async provideInlineCompletions(document, position, context, token) {
document: any,
position: any,
context: any,
token: any
) {
const prompt = this.getPrompt(document, position) const prompt = this.getPrompt(document, position)
const emptyResponse = Promise.resolve({ items: [] }) const emptyResponse = Promise.resolve({ items: [] })
@ -67,7 +60,13 @@ class CompletionProvider {
return emptyResponse return emptyResponse
} }
const response = await this.callTabbyApi(currentTimestamp, prompt) let response
try {
response = await this.callTabbyApi(currentTimestamp, prompt)
} catch (err) {
console.error("error", err)
return emptyResponse
}
const hasSuffixParen = this.hasSuffixParen(document, position) const hasSuffixParen = this.hasSuffixParen(document, position)
const replaceRange = hasSuffixParen const replaceRange = hasSuffixParen
? new this.monaco.Range( ? new this.monaco.Range(
@ -88,7 +87,7 @@ class CompletionProvider {
freeInlineCompletions() {} freeInlineCompletions() {}
getPrompt(document: any, position: any): string { getPrompt(document, position) {
const firstLine = Math.max(position.lineNumber - 120, 0) const firstLine = Math.max(position.lineNumber - 120, 0)
const range = new this.monaco.Range( const range = new this.monaco.Range(
@ -100,17 +99,17 @@ class CompletionProvider {
return document.getValueInRange(range) return document.getValueInRange(range)
} }
isNil(value: any) { isNil(value) {
return value === undefined || value === null || value.length === 0 return value === undefined || value === null || value.length === 0
} }
sleep(milliseconds: number) { sleep(milliseconds) {
return new Promise((r) => setTimeout(r, milliseconds)) return new Promise((r) => setTimeout(r, milliseconds))
} }
async callTabbyApi(timestamp: number, prompt: string) { async callTabbyApi(timestamp, prompt) {
const request = (this.pendingRequest = axios.post( const request = (this.pendingRequest = axios.post(
`${(window as any).tabbyServerURL}/v1/completions`, `${TabbyServerURL}/v1/completions`,
{ {
prompt, prompt,
} }
@ -120,10 +119,10 @@ class CompletionProvider {
return response return response
} }
toInlineCompletions(value: any, range: any) { toInlineCompletions(value, range) {
return ( return (
value.choices value.choices
?.map((choice: any) => choice.text) .map((choice) => choice.text)
.map((text: string) => ({ .map((text: string) => ({
range, range,
text, text,
@ -131,7 +130,7 @@ class CompletionProvider {
) )
} }
hasSuffixParen(document: any, position: any): boolean { hasSuffixParen(document, position) {
const suffix = document.getValueInRange( const suffix = document.getValueInRange(
new this.monaco.Range( new this.monaco.Range(
position.lineNumber, position.lineNumber,

View File

@ -0,0 +1,15 @@
import React from "react"
import ReactDOM from "react-dom/client"
import { StreamlitProvider } from "streamlit-component-lib-react-hooks"
import Monaco from "./Monaco"
const rootElement = document.getElementById("root")
const root = ReactDOM.createRoot(rootElement)
root.render(
<React.StrictMode>
<StreamlitProvider>
<Monaco />
</StreamlitProvider>
</React.StrictMode>
)

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
{
"files": {
"main.js": "./static/js/main.3a9265b0.chunk.js",
"main.js.map": "./static/js/main.3a9265b0.chunk.js.map",
"runtime-main.js": "./static/js/runtime-main.44d30fc2.js",
"runtime-main.js.map": "./static/js/runtime-main.44d30fc2.js.map",
"static/js/2.ae80075f.chunk.js": "./static/js/2.ae80075f.chunk.js",
"static/js/2.ae80075f.chunk.js.map": "./static/js/2.ae80075f.chunk.js.map",
"index.html": "./index.html",
"precache-manifest.1d3088fc135428c2ea5392e379f82710.js": "./precache-manifest.1d3088fc135428c2ea5392e379f82710.js",
"service-worker.js": "./service-worker.js",
"static/js/2.ae80075f.chunk.js.LICENSE.txt": "./static/js/2.ae80075f.chunk.js.LICENSE.txt",
"static/js/main.3a9265b0.chunk.js.LICENSE.txt": "./static/js/main.3a9265b0.chunk.js.LICENSE.txt"
},
"entrypoints": [
"static/js/runtime-main.44d30fc2.js",
"static/js/2.ae80075f.chunk.js",
"static/js/main.3a9265b0.chunk.js"
]
}

View File

@ -1 +0,0 @@
<!doctype html><html lang="en"><head><title>Streamlit Component</title><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Streamlit Component"/><link rel="stylesheet" href="bootstrap.min.css"/></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function t(t){for(var n,l,a=t[0],p=t[1],i=t[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in p)Object.prototype.hasOwnProperty.call(p,n)&&(e[n]=p[n]);for(f&&f(t);s.length;)s.shift()();return u.push.apply(u,i||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var p=r[a];0!==o[p]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="./";var a=this.webpackJsonpstreamlit_component_template=this.webpackJsonpstreamlit_component_template||[],p=a.push.bind(a);a.push=t,a=a.slice();for(var i=0;i<a.length;i++)t(a[i]);var f=p;r()}([])</script><script src="./static/js/2.ae80075f.chunk.js"></script><script src="./static/js/main.3a9265b0.chunk.js"></script></body></html>

View File

@ -1,26 +0,0 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "42fedf3d1999ef9b28619146c4e415cd",
"url": "./index.html"
},
{
"revision": "409a462d1d8f6189ac9a",
"url": "./static/js/2.ae80075f.chunk.js"
},
{
"revision": "df087e3b51cab167e93df344458ea8f1",
"url": "./static/js/2.ae80075f.chunk.js.LICENSE.txt"
},
{
"revision": "ab4b2d8e1d0c358de5ef",
"url": "./static/js/main.3a9265b0.chunk.js"
},
{
"revision": "4e0e34f265fae8f33b01b27ae29d9d6f",
"url": "./static/js/main.3a9265b0.chunk.js.LICENSE.txt"
},
{
"revision": "7e9d84e346ce158d1e50",
"url": "./static/js/runtime-main.44d30fc2.js"
}
]);

View File

@ -1,39 +0,0 @@
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"./precache-manifest.1d3088fc135428c2ea5392e379f82710.js"
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
workbox.core.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("./index.html"), {
blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
!function(e){function t(t){for(var n,l,a=t[0],p=t[1],i=t[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in p)Object.prototype.hasOwnProperty.call(p,n)&&(e[n]=p[n]);for(f&&f(t);s.length;)s.shift()();return u.push.apply(u,i||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var p=r[a];0!==o[p]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="./";var a=this.webpackJsonpstreamlit_component_template=this.webpackJsonpstreamlit_component_template||[],p=a.push.bind(a);a.push=t,a=a.slice();for(var i=0;i<a.length;i++)t(a[i]);var f=p;r()}([]);
//# sourceMappingURL=runtime-main.44d30fc2.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,13 +0,0 @@
import React from "react"
import ReactDOM from "react-dom"
import { StreamlitProvider } from "streamlit-component-lib-react-hooks"
import Tabby from "./Tabby"
ReactDOM.render(
<React.StrictMode>
<StreamlitProvider>
<Tabby />
</StreamlitProvider>
</React.StrictMode>,
document.getElementById("root")
)

View File

@ -1 +0,0 @@
/// <reference types="react-scripts" />

View File

@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": ["src"]
}

File diff suppressed because it is too large Load Diff