mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-12 04:43:32 +02:00
actions: align artifact preview page layout with repo UI
This commit is contained in:
parent
979572f808
commit
c361782af9
@ -6,7 +6,6 @@ package devtest
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
mathRand "math/rand/v2"
|
||||
"net/http"
|
||||
@ -31,12 +30,13 @@ type mockArtifactFile struct {
|
||||
}
|
||||
|
||||
type mockArtifactPreviewTemplateData struct {
|
||||
ArtifactName string
|
||||
Files []mockArtifactPreviewTemplateFile
|
||||
PreviewURL string
|
||||
PreviewRaw string
|
||||
DownloadURL string
|
||||
SelectedPath string
|
||||
ArtifactName string
|
||||
PreviewFiles []mockArtifactPreviewTemplateFile
|
||||
RunURL string
|
||||
PreviewURL string
|
||||
PreviewRawURL string
|
||||
DownloadURL string
|
||||
SelectedPath string
|
||||
}
|
||||
|
||||
type mockArtifactPreviewTemplateFile struct {
|
||||
@ -69,40 +69,6 @@ var mockActionsArtifactFiles = map[string][]mockArtifactFile{
|
||||
},
|
||||
}
|
||||
|
||||
var mockArtifactPreviewTemplate = template.Must(template.New("mock-artifact-preview").Parse(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Artifact Preview</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; margin: 16px; }
|
||||
.layout { display: grid; grid-template-columns: 260px 1fr; gap: 16px; }
|
||||
.files { border: 1px solid #ddd; border-radius: 6px; padding: 8px; }
|
||||
.files a { display: block; padding: 8px; text-decoration: none; color: inherit; border-radius: 4px; }
|
||||
.files a.selected { background: #f0f6ff; }
|
||||
iframe { width: 100%; min-height: 70vh; border: 1px solid #ddd; border-radius: 6px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Preview: {{.ArtifactName}}</h2>
|
||||
<p><a href="{{.PreviewURL}}">Reload</a> | <a href="{{.DownloadURL}}">Download ZIP</a></p>
|
||||
<div class="layout">
|
||||
<div class="files">
|
||||
{{range .Files}}
|
||||
<a class="{{if .Selected}}selected{{end}}" href="{{$.PreviewURL}}?path={{.Path | urlquery}}">{{.Path}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div>
|
||||
{{if .SelectedPath}}
|
||||
<iframe src="{{.PreviewRaw}}/{{.SelectedPath | urlquery}}" sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe>
|
||||
{{else}}
|
||||
<p>No files</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>`))
|
||||
|
||||
func normalizeMockArtifactPath(path string) string {
|
||||
path = util.PathJoinRelX(path)
|
||||
if path == "." {
|
||||
@ -387,19 +353,23 @@ func MockActionsArtifactPreview(ctx *context.Context) {
|
||||
previewRawURL := previewURL + "/raw"
|
||||
downloadURL := fmt.Sprintf("%s/devtest/repo-action-view/runs/%d/artifacts/%s", setting.AppSubURL, runID, url.PathEscape(artifactName))
|
||||
data := mockArtifactPreviewTemplateData{
|
||||
ArtifactName: artifactName,
|
||||
Files: templateFiles,
|
||||
PreviewURL: previewURL,
|
||||
PreviewRaw: previewRawURL,
|
||||
DownloadURL: downloadURL,
|
||||
SelectedPath: selectedPath,
|
||||
ArtifactName: artifactName,
|
||||
PreviewFiles: templateFiles,
|
||||
RunURL: previewURL,
|
||||
PreviewURL: previewURL,
|
||||
PreviewRawURL: previewRawURL,
|
||||
DownloadURL: downloadURL,
|
||||
SelectedPath: selectedPath,
|
||||
}
|
||||
|
||||
ctx.Resp.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := mockArtifactPreviewTemplate.Execute(ctx.Resp, data); err != nil {
|
||||
ctx.ServerError("mockArtifactPreviewTemplate.Execute", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["ArtifactName"] = data.ArtifactName
|
||||
ctx.Data["PreviewFiles"] = data.PreviewFiles
|
||||
ctx.Data["RunURL"] = data.RunURL
|
||||
ctx.Data["PreviewURL"] = data.PreviewURL
|
||||
ctx.Data["PreviewRawURL"] = data.PreviewRawURL
|
||||
ctx.Data["DownloadURL"] = data.DownloadURL
|
||||
ctx.Data["SelectedPath"] = data.SelectedPath
|
||||
ctx.HTML(http.StatusOK, "devtest/repo-action-artifact-preview")
|
||||
}
|
||||
|
||||
func MockActionsArtifactPreviewRaw(ctx *context.Context) {
|
||||
@ -410,7 +380,11 @@ func MockActionsArtifactPreviewRaw(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
selectedPath := chooseMockArtifactPath(files, normalizeMockArtifactPath(ctx.Req.URL.Query().Get("path")))
|
||||
selectedPath := normalizeMockArtifactPath(strings.TrimPrefix(ctx.PathParam("*"), "/"))
|
||||
if selectedPath == "" {
|
||||
selectedPath = normalizeMockArtifactPath(ctx.Req.URL.Query().Get("path"))
|
||||
}
|
||||
selectedPath = chooseMockArtifactPath(files, selectedPath)
|
||||
if selectedPath == "" {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
@ -442,5 +416,6 @@ func MockActionsArtifactPreviewRaw(ctx *context.Context) {
|
||||
ctx.ServeContent(strings.NewReader(selectedFile.Content), context.ServeHeaderOptions{
|
||||
Filename: selectedFile.Path,
|
||||
ContentLength: &size,
|
||||
ContentType: "text/plain; charset=utf-8",
|
||||
})
|
||||
}
|
||||
|
||||
@ -905,7 +905,8 @@ func previewArtifactByReadSeeker(ctx *context_module.Context, path string, reade
|
||||
return
|
||||
}
|
||||
ctx.ServeContent(reader, context_module.ServeHeaderOptions{
|
||||
Filename: path,
|
||||
Filename: path,
|
||||
ContentType: st.GetMimeType(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
28
templates/devtest/repo-action-artifact-preview.tmpl
Normal file
28
templates/devtest/repo-action-artifact-preview.tmpl
Normal file
@ -0,0 +1,28 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content repository actions">
|
||||
<div class="ui fluid container artifact-preview-page">
|
||||
{{template "shared/actions/artifact_preview_content" .}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.artifact-preview-page {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.artifact-preview-title {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.artifact-preview-subtitle {
|
||||
color: var(--color-text-light-3);
|
||||
}
|
||||
|
||||
.artifact-preview-frame {
|
||||
width: 100%;
|
||||
min-height: 70vh;
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
</style>
|
||||
{{template "base/footer" .}}
|
||||
@ -3,43 +3,9 @@
|
||||
<div class="page-content repository actions">
|
||||
{{template "repo/header" .}}
|
||||
|
||||
<div class="ui container artifact-preview-page">
|
||||
<div class="artifact-preview-header">
|
||||
<div class="artifact-preview-title">
|
||||
<h2 class="ui header">{{ctx.Locale.Tr "preview"}}: <span class="gt-ellipsis">{{.ArtifactName}}</span></h2>
|
||||
<div class="artifact-preview-subtitle">
|
||||
<a href="{{.RunURL}}">{{ctx.Locale.Tr "go_back"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="ui button" href="{{.DownloadURL}}">
|
||||
{{svg "octicon-download"}}
|
||||
{{ctx.Locale.Tr "repo.download_file"}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="ui stackable grid">
|
||||
<div class="four wide column">
|
||||
<div class="ui fluid vertical menu">
|
||||
<div class="item">
|
||||
<b>{{ctx.Locale.Tr "files"}}</b>
|
||||
</div>
|
||||
{{range .PreviewFiles}}
|
||||
<a class="item gt-ellipsis {{if .Selected}}active{{end}}" href="{{$.PreviewURL}}?path={{QueryEscape .Path}}" title="{{.Path}}">
|
||||
{{.Path}}
|
||||
</a>
|
||||
{{else}}
|
||||
<div class="item disabled">{{ctx.Locale.Tr "none"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="twelve wide column">
|
||||
{{if .SelectedPath}}
|
||||
<iframe class="artifact-preview-frame" src="{{.PreviewRawURL}}/{{PathEscapeSegments .SelectedPath}}" sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe>
|
||||
{{else}}
|
||||
<div class="ui attached segment">{{ctx.Locale.Tr "none"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui fluid container artifact-preview-page">
|
||||
{{template "base/alert" .}}
|
||||
{{template "shared/actions/artifact_preview_content" .}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -48,22 +14,10 @@
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.artifact-preview-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.artifact-preview-title {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.artifact-preview-title .ui.header {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.artifact-preview-subtitle {
|
||||
color: var(--color-text-light-3);
|
||||
}
|
||||
|
||||
38
templates/shared/actions/artifact_preview_content.tmpl
Normal file
38
templates/shared/actions/artifact_preview_content.tmpl
Normal file
@ -0,0 +1,38 @@
|
||||
<div class="ui top attached header tw-flex tw-items-center tw-justify-between">
|
||||
<div class="artifact-preview-title">
|
||||
<span class="tw-text-base tw-font-semibold">{{ctx.Locale.Tr "preview"}}: <span class="gt-ellipsis">{{.ArtifactName}}</span></span>
|
||||
<div class="artifact-preview-subtitle tw-mt-1">
|
||||
<a href="{{.RunURL}}">{{ctx.Locale.Tr "go_back"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="ui button" href="{{.DownloadURL}}">
|
||||
{{svg "octicon-download"}}
|
||||
{{ctx.Locale.Tr "repo.download_file"}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="ui attached segment">
|
||||
<div class="ui stackable grid">
|
||||
<div class="four wide column">
|
||||
<div class="ui fluid vertical menu">
|
||||
<div class="item">
|
||||
<b>{{ctx.Locale.Tr "files"}}</b>
|
||||
</div>
|
||||
{{range .PreviewFiles}}
|
||||
<a class="item gt-ellipsis {{if .Selected}}active{{end}}" href="{{$.PreviewURL}}?path={{QueryEscape .Path}}" title="{{.Path}}">
|
||||
{{.Path}}
|
||||
</a>
|
||||
{{else}}
|
||||
<div class="item disabled">{{ctx.Locale.Tr "none"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="twelve wide column">
|
||||
{{if .SelectedPath}}
|
||||
<iframe class="artifact-preview-frame" src="{{.PreviewRawURL}}/{{PathEscapeSegments .SelectedPath}}" sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe>
|
||||
{{else}}
|
||||
<div class="ui attached segment">{{ctx.Locale.Tr "none"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -133,12 +133,12 @@ async function deleteArtifact(name: string) {
|
||||
<template v-for="artifact in artifacts" :key="artifact.name">
|
||||
<li class="job-artifacts-item">
|
||||
<template v-if="artifact.status !== 'expired'">
|
||||
<a class="flex-text-inline" target="_blank" :href="artifactPreviewURL(artifact.name)">
|
||||
<a class="flex-text-inline" :href="artifactPreviewURL(artifact.name)">
|
||||
<SvgIcon name="octicon-file" class="tw-text-text"/>
|
||||
<span class="gt-ellipsis">{{ artifact.name }}</span>
|
||||
</a>
|
||||
<span class="job-artifact-actions">
|
||||
<a download target="_blank" :href="artifactDownloadURL(artifact.name)" :data-tooltip-content="locale.downloadFile">
|
||||
<a download :href="artifactDownloadURL(artifact.name)" :data-tooltip-content="locale.downloadFile">
|
||||
<SvgIcon name="octicon-download" class="tw-text-text"/>
|
||||
</a>
|
||||
<a v-if="run.canDeleteArtifact" @click="deleteArtifact(artifact.name)">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user