diff --git a/mock/mock-response.jsonl b/mock/mock-response.jsonl index f1ae9ef..5783283 100644 --- a/mock/mock-response.jsonl +++ b/mock/mock-response.jsonl @@ -1,2 +1,7 @@ +{"type":"log","level":"trace","message":"Starting JCR Hopper with 6 parameters, for 4 of which arguments were passed: []"} +{"type":"log","level":"debug","message":"JCR Hopper script started at 12345"} +"Some plain\ntext output" +{"type":"file","name":"Test.csv","mime":"text/csv;charset=utf-8","data":"b25lLHR3byx0aHJlZQoxLDIsMwo="} {"type":"log","level":"info","message":"JCR Hopper script finished after 0ms"} -{"type":"log","level":"warn","message":"Not saving changes as dry run is enabled"} \ No newline at end of file +{"type":"log","level":"warn","message":"Not saving changes as dry run is enabled"} +{"type":"log","level":"error","message":"Script execution aborted with exception","exception":"javax.jcr.query.InvalidQueryException: java.text.ParseException: Query: SELECT * FROM [nt:unstructured] as text WHERE text.[sling:resourceType] =(*); expected: static operand\norg.apache.jackrabbit.oak.jcr.query.QueryManagerImpl.parse(QueryManagerImpl.java:127)\norg.apache.jackrabbit.oak.jcr.query.QueryImpl$1.perform(QueryImpl.java:87)\norg.apache.jackrabbit.oak.jcr.query.QueryImpl$1.perform(QueryImpl.java:83)\norg.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.perform(SessionDelegate.java:207)\norg.apache.jackrabbit.oak.jcr.query.QueryImpl.parse(QueryImpl.java:82)\norg.apache.jackrabbit.oak.jcr.query.QueryImpl.getBindVariableNames(QueryImpl.java:112)\ncom.swisscom.aem.tools.impl.hops.NodeQuery.getQueryResult(NodeQuery.java:83)\ncom.swisscom.aem.tools.impl.hops.NodeQuery.run(NodeQuery.java:36)\ncom.swisscom.aem.tools.impl.hops.NodeQuery.run(NodeQuery.java:28)\ncom.swisscom.aem.tools.impl.HopContextImpl.runHop(HopContextImpl.java:88)\ncom.swisscom.aem.tools.impl.RunnerImpl.run(RunnerImpl.java:90)\ncom.swisscom.aem.tools.impl.RunnerImpl.run(RunnerImpl.java:64)\ncom.swisscom.aem.tools.impl.http.HopRunnerServlet.doPost(HopRunnerServlet.java:90)\norg.apache.sling.api.servlets.SlingAllMethodsServlet.mayService(SlingAllMethodsServlet.java:146)\norg.apache.sling.api.servlets.SlingSafeMethodsServlet.service(SlingSafeMethodsServlet.java:342)\norg.apache.sling.api.servlets.SlingSafeMethodsServlet.service(SlingSafeMethodsServlet.java:374)\norg.apache.sling.engine.impl.request.RequestData.service(RequestData.java:545)\norg.apache.sling.engine.impl.filter.SlingComponentFilterChain.render(SlingComponentFilterChain.java:45)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:88)\ncom.day.cq.wcm.core.impl.WCMDebugFilter.doFilter(WCMDebugFilter.java:156)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\ncom.day.cq.wcm.core.impl.WCMComponentFilter.filterRootInclude(WCMComponentFilter.java:375)\ncom.day.cq.wcm.core.impl.WCMComponentFilter.doFilter(WCMComponentFilter.java:190)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.day.cq.wcm.core.impl.page.PageLockFilter.doFilter(PageLockFilter.java:91)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.mobile.filter.DesktopMobileSwitchFilter.doFilter(DesktopMobileSwitchFilter.java:67)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.swisscom.portal.components.servlets.GeneratedSampleFocusFilter.doFilter(GeneratedSampleFocusFilter.java:41)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.day.cq.personalization.impl.TargetComponentFilter.doFilter(TargetComponentFilter.java:94)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.swisscom.portal.components.servlets.RectifyResourceResolverOfIncludesFilter.doFilter(RectifyResourceResolverOfIncludesFilter.java:32)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.SlingRequestProcessorImpl.processComponent(SlingRequestProcessorImpl.java:367)\norg.apache.sling.engine.impl.filter.RequestSlingFilterChain.render(RequestSlingFilterChain.java:49)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:82)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\ncom.day.cq.dam.core.impl.assetlinkshare.AdhocAssetShareAuthHandler.doFilter(AdhocAssetShareAuthHandler.java:440)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.cognifide.cq.includefilter.SyntheticResourceIncludingFilter.doFilter(SyntheticResourceIncludingFilter.java:47)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\ncom.day.cq.wcm.core.impl.warp.TimeWarpFilter.doFilter(TimeWarpFilter.java:109)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.cq.social.ugcbase.security.impl.SaferSlingPostServlet.doFilter(SaferSlingPostServlet.java:126)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\ncom.day.cq.dam.core.impl.servlet.ActivityRecordHandler.doFilter(ActivityRecordHandler.java:141)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.granite.rest.impl.servlet.ApiResourceFilter.doFilter(ApiResourceFilter.java:70)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.granite.requests.logging.impl.RequestLoggerImpl.doFilter(RequestLoggerImpl.java:121)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.granite.rest.assets.impl.AssetContentDispositionFilter.doFilter(AssetContentDispositionFilter.java:96)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.day.cq.wcm.core.impl.AuthoringUIModeServiceImpl.doFilter(AuthoringUIModeServiceImpl.java:394)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.day.cq.wcm.mobile.core.impl.redirect.RedirectFilter.doFilter(RedirectFilter.java:248)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.swisscom.portal.aem.metadata.impl.CacheWarmupFilter.doFilter(CacheWarmupFilter.java:39)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.services.MimeTypeFilter.doFilter(MimeTypeFilter.java:55)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\ncom.cognifide.cq.includefilter.CacheControlFilter.doFilter(CacheControlFilter.java:43)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\norg.apache.sling.engine.impl.debug.RequestProgressTrackerLogFilter.doFilter(RequestProgressTrackerLogFilter.java:109)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\nch.swisscom.portal.base.clientlib.CompressedAssetFilter.doFilter(CompressedAssetFilter.java:109)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\ncom.adobe.cq.social.commons.cors.CORSAuthenticationFilter.doFilter(CORSAuthenticationFilter.java:91)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.day.cq.wcm.foundation.forms.FormsHandlingServletHelper.handleFilter(FormsHandlingServletHelper.java:226)\ncom.day.cq.wcm.foundation.forms.impl.FormsHandlingServlet.doFilter(FormsHandlingServlet.java:138)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.granite.optout.impl.OptOutFilter.doFilter(OptOutFilter.java:76)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.day.cq.wcm.foundation.forms.FormsHandlingServletHelper.handleFilter(FormsHandlingServletHelper.java:226)\ncom.adobe.cq.wcm.core.components.internal.servlets.CoreFormHandlingServlet.doFilter(CoreFormHandlingServlet.java:123)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.forms.business.FormHandlingFilter.doFilter(FormHandlingFilter.java:84)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.fetcher.filter.ResourceFetcherFilter.doFilter(ResourceFetcherFilter.java:88)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.services.AdditionalHeaderFilter.doFilter(AdditionalHeaderFilter.java:91)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.services.DamIndexingFilter.doFilter(DamIndexingFilter.java:66)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.business.pex.PexAuthorizationFilter.doFilter(PexAuthorizationFilter.java:73)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.acs.commons.wcm.impl.AemEnvironmentIndicatorFilter$InnerEnvironmentIndicatorFilter.doFilter(AemEnvironmentIndicatorFilter.java:463)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.legacyurl.NoExtensionURLFilter.doFilter(NoExtensionURLFilter.java:60)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.day.cq.wcm.core.impl.WCMRequestFilter.doFilter(WCMRequestFilter.java:90)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.cq.history.impl.HistoryRequestFilter.doFilter(HistoryRequestFilter.java:122)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.rewriter.impl.RewriterFilter.doFilter(RewriterFilter.java:87)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.services.LanguageCopyFilter.doFilter(LanguageCopyFilter.java:107)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.granite.httpcache.impl.InnerCacheFilter.doFilter(InnerCacheFilter.java:81)\ncom.adobe.granite.httpcache.impl.InnerCacheFilter.doFilter(InnerCacheFilter.java:60)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.i18n.impl.I18NFilter.doFilter(I18NFilter.java:131)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\ncom.adobe.granite.csrf.impl.CSRFFilter.doFilter(CSRFFilter.java:217)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.security.impl.ContentDispositionFilter.doFilter(ContentDispositionFilter.java:152)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\nch.swisscom.portal.base.performance.RequestProgressLogFilter.doFilter(RequestProgressLogFilter.java:64)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:84)\ncom.adobe.granite.resourceresolverhelper.impl.ResourceResolverHelperImpl.doFilter(ResourceResolverHelperImpl.java:83)\norg.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:78)\norg.apache.sling.engine.impl.SlingRequestProcessorImpl.doProcessRequest(SlingRequestProcessorImpl.java:240)\norg.apache.sling.engine.impl.SlingMainServlet.service(SlingMainServlet.java:130)\norg.apache.felix.http.base.internal.handler.ServletHandler.handle(ServletHandler.java:127)\norg.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:86)\ncom.adobe.acs.commons.wcm.impl.AemEnvironmentIndicatorFilter.doFilter(AemEnvironmentIndicatorFilter.java:208)\norg.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:149)\norg.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:81)\ncom.adobe.granite.license.impl.LicenseCheckFilter.doFilter(LicenseCheckFilter.java:308)\norg.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:149)\norg.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:81)\norg.apache.sling.i18n.impl.I18NFilter.doFilter(I18NFilter.java:131)\norg.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:149)\norg.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:81)\norg.apache.sling.featureflags.impl.FeatureManager.doFilter(FeatureManager.java:116)\norg.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:149)\norg.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:81)\norg.apache.sling.engine.impl.log.RequestLoggerFilter.doFilter(RequestLoggerFilter.java:73)\norg.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:149)\norg.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:81)\norg.apache.sling.engine.impl.parameters.RequestParameterSupportConfigurer.doFilter(RequestParameterSupportConfigurer.java:67)\norg.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:149)\norg.apache.felix.http.base.internal.dispatch.InvocationChain.doFilter(InvocationChain.java:81)\norg.apache.felix.http.base.internal.dispatch.Dispatcher$1.doFilter(Dispatcher.java:152)\norg.apache.felix.http.base.internal.whiteboard.WhiteboardManager$2.doFilter(WhiteboardManager.java:992)\ncom.adobe.granite.auth.oauth.impl.OAuthCallbackFilter.doFilter(OAuthCallbackFilter.java:78)\norg.apache.felix.http.base.internal.handler.PreprocessorHandler.handle(PreprocessorHandler.java:137)\norg.apache.felix.http.base.internal.whiteboard.WhiteboardManager$2.doFilter(WhiteboardManager.java:998)\norg.apache.sling.security.impl.ReferrerFilter.doFilter(ReferrerFilter.java:326)\norg.apache.felix.http.base.internal.handler.PreprocessorHandler.handle(PreprocessorHandler.java:137)\norg.apache.felix.http.base.internal.whiteboard.WhiteboardManager$2.doFilter(WhiteboardManager.java:998)\norg.apache.felix.http.sslfilter.internal.SslFilter.doFilter(SslFilter.java:97)\norg.apache.felix.http.base.internal.handler.PreprocessorHandler.handle(PreprocessorHandler.java:137)\norg.apache.felix.http.base.internal.whiteboard.WhiteboardManager$2.doFilter(WhiteboardManager.java:998)\norg.apache.sling.engine.impl.log.RequestLoggerPreprocessor.doFilter(RequestLoggerPreprocessor.java:47)\norg.apache.felix.http.base.internal.handler.PreprocessorHandler.handle(PreprocessorHandler.java:137)\norg.apache.felix.http.base.internal.whiteboard.WhiteboardManager$2.doFilter(WhiteboardManager.java:998)\norg.apache.felix.http.base.internal.whiteboard.WhiteboardManager.invokePreprocessors(WhiteboardManager.java:1002)\norg.apache.felix.http.base.internal.dispatch.Dispatcher.dispatch(Dispatcher.java:94)\norg.apache.felix.http.base.internal.dispatch.DispatcherServlet.service(DispatcherServlet.java:49)\njavax.servlet.http.HttpServlet.service(HttpServlet.java:764)\norg.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799)\norg.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:554)\norg.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)\norg.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624)\norg.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)\norg.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)\norg.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505)\norg.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)\norg.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355)\norg.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)\norg.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:191)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\norg.eclipse.jetty.server.Server.handle(Server.java:516)\norg.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)\norg.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)\norg.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)\norg.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)\norg.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)\norg.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)\norg.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)\norg.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338)\norg.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315)\norg.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173)\norg.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)\norg.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409)\norg.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)\njava.lang.Thread.run(Thread.java:750)"} \ No newline at end of file diff --git a/src/main/frontend/App.tsx b/src/main/frontend/App.tsx index a20c524..0c2a104 100644 --- a/src/main/frontend/App.tsx +++ b/src/main/frontend/App.tsx @@ -68,6 +68,10 @@ const RootElement = styled('div')` grid-column: span 2; } } + + fieldset { + border-radius: 6px; + } `; export const App: FC<{ runEndpoint: string }> = props => { diff --git a/src/main/frontend/coral/custom-elements.d.ts b/src/main/frontend/coral/custom-elements.d.ts index fbc19b1..79c789a 100644 --- a/src/main/frontend/coral/custom-elements.d.ts +++ b/src/main/frontend/coral/custom-elements.d.ts @@ -789,7 +789,10 @@ declare module 'react' { 'coral-checkbox-label': DetailedHTMLProps & { class?: string }, HTMLElement>; 'coral-switch': DetailedHTMLProps & { class?: string }, HTMLInputElement>; 'coral-wait': DetailedHTMLProps & { class?: string }, HTMLElement>; - 'coral-icon': DetailedHTMLProps & { icon: CoralIcon; class?: string }, HTMLElement>; + 'coral-icon': DetailedHTMLProps< + HTMLAttributes & { icon: CoralIcon; size?: CoralIconSize; class?: string }, + HTMLElement + >; 'coral-selectlist': DetailedHTMLProps & { class?: string }, HTMLSelectElement>; 'coral-selectlist-item': DetailedHTMLProps & { class?: string }, HTMLSelectElement>; 'coral-popover': DetailedHTMLProps< diff --git a/src/main/frontend/model/Run.ts b/src/main/frontend/model/Run.ts index 95a4808..94941fb 100644 --- a/src/main/frontend/model/Run.ts +++ b/src/main/frontend/model/Run.ts @@ -7,14 +7,18 @@ export interface LogMessage { exception?: string; } +export type MimeType = `${'text' | 'application' | 'image' | 'video' | 'audio'}/${string}` | `text/${string};charset=utf-8`; + export interface FileMessage { type: 'file'; name: string; - mime: string; + mime: MimeType; data?: string; blob?: Blob; } +export type PrintMessage = string; + function decodeBase64(base64: string) { const raw = window.atob(base64); const rawLength = raw.length; @@ -54,8 +58,6 @@ async function* streamingFetch(response: Response) { } } -type PrintMessage = string; - export type Message = LogMessage | FileMessage | PrintMessage; export class Run { @@ -65,12 +67,12 @@ export class Run { public messageHandler?: (message: Message | null) => void; - constructor(response: Response) { + constructor(response: Promise) { this.loadMessages(response).catch(e => console.error('Error loading messages', e)); } - private async loadMessages(response: Response) { - for await (const message of streamingFetch(response)) { + private async loadMessages(response: Promise) { + for await (const message of streamingFetch(await response)) { if (typeof message !== 'string') { if (message.type === 'file' && message.data) { message.blob = new Blob([decodeBase64(message.data)], { type: message.mime }); diff --git a/src/main/frontend/sections/Runner.tsx b/src/main/frontend/sections/Runner.tsx index d19f6a7..e0c9501 100644 --- a/src/main/frontend/sections/Runner.tsx +++ b/src/main/frontend/sections/Runner.tsx @@ -11,7 +11,7 @@ export const Runner: FC = () => { const [runs, setRuns] = useState([]); async function runWith(data: FormData) { - const response = await fetch(endpoint, { + const response = fetch(endpoint, { method: 'POST', body: data, }); diff --git a/src/main/frontend/sections/output/FileMessageOutput.tsx b/src/main/frontend/sections/output/FileMessageOutput.tsx new file mode 100644 index 0000000..061405c --- /dev/null +++ b/src/main/frontend/sections/output/FileMessageOutput.tsx @@ -0,0 +1,87 @@ +import React, { FC } from 'react'; + +import { styled } from 'goober'; + +import { FileMessage, MimeType } from '../../model/Run'; +import { CoralIcon } from '../../coral/custom-elements'; + +const Elm = styled('div')` + background: #666; + color: white; + padding: 1em; + white-space: pre-wrap; + word-break: break-all; + border: 2px solid #0002; + border-radius: 12px; + display: grid; + gap: 6px; + grid-template-areas: 'icon name . download' 'icon type . download'; + grid-template-columns: max-content max-content 1fr max-content; + > coral-icon { + grid-area: icon; + position: relative; + top: 0.3em; + } + > .name { + grid-area: name; + } + > .type { + grid-area: type; + } + > a { + grid-area: download; + } +`; + +function iconFor(mimeType: MimeType): CoralIcon { + const [kind, ...rest] = mimeType.split('/'); + let [type] = rest; + [type] = type!.split(';'); + let icon: CoralIcon = 'file'; + if (kind === 'application') { + icon = 'fileCode'; + if (type === 'pdf') { + icon = 'filePDF'; + } + if (type === 'json') { + icon = 'fileJson'; + } + if (type === 'xhtml+xml') { + icon = 'fileHTML'; + } + if (['zip', 'vnd.rar', 'x-compressed', 'x-gzip-compressed'].includes(type!)) { + icon = 'fileZip'; + } + } + if (kind === 'text') { + icon = 'fileTxt'; + if (type === 'csv') { + icon = 'fileCSV'; + } + if (type === 'html') { + icon = 'fileHTML'; + } + } + if (kind === 'image') { + icon = 'image'; + } + + return icon; +} + +export const FileMessageOutput: FC<{ message: FileMessage }> = ({ message }) => { + const icon = iconFor(message.mime); + + return ( + + + {message.name} + {message.mime} + + + + + ); +}; diff --git a/src/main/frontend/sections/output/LogMessageOutput.tsx b/src/main/frontend/sections/output/LogMessageOutput.tsx new file mode 100644 index 0000000..2ea2f6d --- /dev/null +++ b/src/main/frontend/sections/output/LogMessageOutput.tsx @@ -0,0 +1,76 @@ +import React, { FC } from 'react'; + +import { styled } from 'goober'; +import { useToggle } from '@uidotdev/usehooks'; + +import { LogMessage } from '../../model/Run'; + +const Elm = styled('div')` + display: grid; + grid-template-columns: min-content 1fr min-content; + align-items: baseline; + gap: 6px; + > .level { + text-transform: uppercase; + display: inline-block; + border: 1px solid #00000022; + padding: 0 0.3em; + border-radius: 8px; + background: #ddebf7; + &.debug { + background: #5b9bd5; + } + &.info { + background: #70ad47; + } + &.warn { + background: #ffc000; + } + &.error { + background: #ff3300; + color: white; + } + } + > .message { + word-break: break-all; + } + > .ex-toggle { + font-size: 1.4em; + font-weight: bold; + background: #ff3300; + color: white; + display: inline-block; + border: 0; + aspect-ratio: 1; + border-radius: 50%; + padding: 0 6px; + text-align: center; + } + &:has(.exception) > .ex-toggle { + color: #ff3300; + background: white; + } + > .exception { + grid-column: span 3; + white-space: pre-wrap; + word-break: break-all; + font-family: monospace; + } +`; + +export const LogMessageOutput: FC<{ message: LogMessage }> = ({ message }) => { + const [expanded, toggleExpanded] = useToggle(false); + + return ( + + {message.level} + {message.message} + {message.exception ? ( + + ) : undefined} + {expanded ?
{message.exception}
: undefined} +
+ ); +}; diff --git a/src/main/frontend/sections/output/MessageOutput.tsx b/src/main/frontend/sections/output/MessageOutput.tsx index ce0049d..e3c9941 100644 --- a/src/main/frontend/sections/output/MessageOutput.tsx +++ b/src/main/frontend/sections/output/MessageOutput.tsx @@ -1,7 +1,16 @@ import React, { FC } from 'react'; import { Message } from '../../model/Run'; +import { PrintMessageOutput } from './PrintMessageOutput'; +import { FileMessageOutput } from './FileMessageOutput'; +import { LogMessageOutput } from './LogMessageOutput'; export const MessageOutput: FC<{ message: Message }> = ({ message }) => { - return
{JSON.stringify(message)}
; + if (typeof message === 'string') { + return ; + } + if (message.type === 'file') { + return ; + } + return ; }; diff --git a/src/main/frontend/sections/output/PrintMessageOutput.tsx b/src/main/frontend/sections/output/PrintMessageOutput.tsx new file mode 100644 index 0000000..48399ea --- /dev/null +++ b/src/main/frontend/sections/output/PrintMessageOutput.tsx @@ -0,0 +1,18 @@ +import React, { FC } from 'react'; + +import { styled } from 'goober'; + +import { PrintMessage } from '../../model/Run'; + +const Elm = styled('div')` + background: #ccc; + padding: 0.4em; + white-space: pre-wrap; + word-break: break-all; + border: 2px solid #0002; + border-radius: 12px; +`; + +export const PrintMessageOutput: FC<{ message: PrintMessage }> = ({ message }) => { + return {message}; +}; diff --git a/src/main/frontend/sections/output/RunOutput.tsx b/src/main/frontend/sections/output/RunOutput.tsx index bb9e4ed..94d97a4 100644 --- a/src/main/frontend/sections/output/RunOutput.tsx +++ b/src/main/frontend/sections/output/RunOutput.tsx @@ -4,7 +4,32 @@ import { styled } from 'goober'; import { Run } from '../../model/Run'; import { MessageOutput } from './MessageOutput'; -const Elm = styled('div')``; +const Elm = styled('details')` + border: 1px solid #c0c0c0; + border-radius: 6px; + padding: 1em; + margin-bottom: 6px; + > summary { + display: grid; + align-items: baseline; + grid-auto-flow: column; + grid-template-columns: 22px 1fr; + &::before { + content: '▶'; + } + h3 { + margin: 0; + } + margin: 0.5em 0; + } + &[open] > summary::before { + content: '▼'; + } + > .content { + display: grid; + gap: 6px; + } +`; export const RunOutput: FC<{ run: Run }> = ({ run }) => { const [messages, setMessages] = useState(run.messages); @@ -21,19 +46,24 @@ export const RunOutput: FC<{ run: Run }> = ({ run }) => { }, [run]); return ( - -

- {finished ? undefined : ( - <> - {' '} - - )} - {run.started.toLocaleString('de-CH')} -

- - {messages.map((msg, i) => ( - - ))} + + +

+ {finished ? ( + + ) : ( + <> + {' '} + + )} + {run.started.toLocaleString()} +

+
+
+ {messages.map((msg, i) => ( + + ))} +
); }; diff --git a/src/main/frontend/widgets/StepEditor.tsx b/src/main/frontend/widgets/StepEditor.tsx index bc23d48..262a0b0 100644 --- a/src/main/frontend/widgets/StepEditor.tsx +++ b/src/main/frontend/widgets/StepEditor.tsx @@ -59,7 +59,7 @@ const Elm = styled('div')` --contrast-color: black; } - details { + > details { border-radius: 4px; background-color: var(--accent-color);