feat(repository): add support for all kind of images to RepositoryBrowserView@@ -1,5 +1,5 @@
{
- "_generatedAtUnix": 1663726257814,
+ "_generatedAtUnix": 1663770423607,
"_hashAlgorithm": "sha1",
"_version": 2,
"islands": {
@@ -50,7 +50,7 @@
"pathSource": "./app/views/auth/RegisterView.tsx"
},
"RepositoryBrowserView": {
- "hash": "e21209cae1f4aaab6678ac937259c085084abbff",
+ "hash": "d9f8ac892f78a75e1f7f2a1cb82602fb87dafd72",
"pathSource": "./app/views/repository/RepositoryBrowserView.tsx"
},
"RepositoryCommitsLogView": {
@@ -1,8 +1,12 @@
// 3rd-party
import { FastifyPluginAsync } from "fastify";
import fp from "fastify-plugin";
+import getFilePathExt from "file-extension";
+import imageExts from "image-extensions";
+import isImageFilePath from "is-image";
import languagesMap from "language-map";
import languageDetect from "language-detect";
+import mimeTypes from "mime-db";
// app
import { makeCodeAnalysisService } from "../services/codeAnalysis";
@@ -10,8 +14,27 @@ const serviceKey = "codeAnalysisService";
export const codeAnalysisPlugin: FastifyPluginAsync<never> = fp(
async (server) => {
const service = makeCodeAnalysisService({
+ getFilePathExt,
+ imageExts,
+ isImageFilePath,
languageDetect,
languagesMap,
+ extsMimeTypesMap: Object.entries(mimeTypes).reduce(
+ (acc, [mimeType, metas]) => {
+ const { extensions } = metas;
+ if (extensions == null) return acc;
+ extensions.forEach((ext) => {
+ acc = {
+ ...acc,
+ [ext]: mimeType,
+ };
+ });
+ return acc;
+ },
+ {} as {
+ [extension: string]: string; // ext => mime-type
+ }
+ ),
});
server.decorate(serviceKey, {
getter: () => service,
@@ -9,6 +9,20 @@ const makeGetLinguistFileInfos: ServiceMethodFactory<
Promise<LinguistFileInfos>
> = (deps) => {
return async (path, content = undefined) => {
+ const ext = deps.getFilePathExt(path);
+ if (deps.imageExts.includes(ext)) {
+ return {
+ // TODO(improvement): make an algorithm to generate one color per ext?
+ color: "gray",
+ extensions: [ext],
+ languageDisplayName: ext.toUpperCase(),
+ language: ext,
+ languageId: -1,
+ mimeType: deps.extsMimeTypesMap[ext] || `image/${ext}`,
+ type: "image",
+ };
+ }
+
const language =
content != null
? deps.languageDetect.contents(path, content)
@@ -23,8 +23,10 @@ export interface CodeAnalysisServiceAPI extends ServiceApiContract {
}
export interface CodeAnalysisServiceDeps {
+ extsMimeTypesMap: { [extension: string]: string };
+ getFilePathExt: (path: string) => string;
+ imageExts: string[];
+ isImageFilePath: (path: string) => boolean;
languageDetect: LanguageDetectFn;
-
- // types for NPM package's: language-map
languagesMap: LanguagesMap;
}
@@ -8,7 +8,13 @@ import type { Organization, Repository, User } from "@prisma/client";
import type { LinguistFileInfos } from "../../services/codeAnalysis/types";
// app
import type { CommonProps, RepositoryFileContent } from "../../types";
-import { Code, Layout, PageWrapper, getThemedCodeCss } from "../../components";
+import {
+ Code,
+ Grid,
+ Layout,
+ PageWrapper,
+ getThemedCodeCss,
+} from "../../components";
export interface RepositoryBrowserViewProps extends CommonProps {
currentUser: null | User;
@@ -42,15 +48,37 @@ const RepositoryBrowserView: ReactView<RepositoryBrowserViewProps> = ({
{" / "}
{path}
</h1>
- <div style={{ width: "100%" }}>
- <span>lang: {linguistInfos.language}</span>
- {getThemedCodeCss(commonProps.themeScheme)}
- <Code
- code={fileContent.content}
- language={linguistInfos.language}
- themeScheme={commonProps.themeScheme}
- />
- </div>
+ <Grid.Col fluid>
+ <Grid.Row
+ nowrap
+ alignItems={"center"}
+ style={{ width: "100%", marginTop: 16 }}
+ >
+ <div>
+ <strong>lang:</strong> <span>{linguistInfos.language}</span>
+ </div>
+ <div style={{ marginLeft: 16 }}>
+ <strong>mime:</strong> <span>{linguistInfos.mimeType}</span>
+ </div>
+ </Grid.Row>
+ {linguistInfos.type === "image" ? (
+ <img
+ src={`data:image/${linguistInfos.mimeType};base64,${Buffer.from(
+ fileContent.content
+ ).toString("base64")}`}
+ style={{ width: "100%", height: "auto" }}
+ />
+ ) : (
+ <>
+ {getThemedCodeCss(commonProps.themeScheme)}
+ <Code
+ code={fileContent.content}
+ language={linguistInfos.language}
+ themeScheme={commonProps.themeScheme}
+ />
+ </>
+ )}
+ </Grid.Col>
</PageWrapper>
</Layout>
);
@@ -38,11 +38,15 @@
"dotenv-flow": "^3.2.0",
"fastify": "^3.27.4",
"fastify-static": "^4.6.1",
+ "file-extension": "^4.0.5",
"gray-matter": "^4.0.3",
+ "image-extensions": "^1.1.0",
+ "is-image": "^3.1.0",
"language-detect": "^1.1.0",
"language-map": "^1.5.0",
"markdown-to-jsx": "^7.1.7",
"markdown-toc": "^1.2.0",
+ "mime-db": "^1.52.0",
"patch-package": "^6.4.7",
"pg": "^8.7.3",
"postinstall-postinstall": "^2.1.0",
@@ -58,6 +62,7 @@
"@types/dotenv-flow": "^3.2.0",
"@types/fastify-static": "^2.2.1",
"@types/jest": "^28.1.6",
+ "@types/mime-db": "^1.43.1",
"@types/node": "^18.6.1",
"@types/pg": "^8.6.5",
"@types/prismjs": "^1.26.0",
@@ -0,0 +1,5 @@
+declare module "file-extension";
+
+export declare const getFilePathExtension: (path: string) => string;
+
+export default getFilePathExtension;
@@ -0,0 +1,5 @@
+declare module "image-extensions";
+
+export declare const imageExtensions: string[];
+
+export default imageExtensions;
@@ -989,6 +989,10 @@
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
+"@types/mime-db@^1.43.1":
+ version "1.43.1"
+ resolved "https://registry.yarnpkg.com/@types/mime-db/-/mime-db-1.43.1.tgz#c2a0522453bb9b6e84ee48b7eef765d19bcd519e"
+
"@types/mime@*":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
@@ -2156,6 +2160,10 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
+file-extension@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/file-extension/-/file-extension-4.0.5.tgz#ae6cef34c28e7313a92baa4aa955755cacdf0ce3"
+
fill-range@^2.1.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
@@ -2577,6 +2585,10 @@ iconv-lite@0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+image-extensions@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/image-extensions/-/image-extensions-1.1.0.tgz#b8e6bf6039df0056e333502a00b6637a3105d894"
+
import-local@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
@@ -2692,6 +2704,10 @@ is-glob@^4.0.1, is-glob@~4.0.1:
dependencies:
is-extglob "^2.1.1"
+is-image@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/is-image/-/is-image-3.1.0.tgz#d8e5e8370474297cba8ad259ec6f213ed506133e"
+
is-negative-zero@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
@@ -3653,7 +3669,7 @@ micromatch@^4.0.2, micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
-mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"