General improvements and bug fixes
This commit is contained in:
parent
bdd46a530c
commit
8f9ec73caf
32
package.json
32
package.json
@ -14,35 +14,35 @@
|
||||
"@types/chai-as-promised": "^7.1.4",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/mocha": "github:whitecolor/mocha-types#da22474cf43f48a56c86f8c23a5a0ea36e295768",
|
||||
"@types/node": "^16.10.3",
|
||||
"@types/sinon": "^10.0.4",
|
||||
"@types/node": "^16.11.9",
|
||||
"@types/sinon": "^10.0.6",
|
||||
"@types/sinon-chai": "^3.2.5",
|
||||
"@types/wicg-file-system-access": "^2020.9.4",
|
||||
"@typescript-eslint/eslint-plugin": "4.33.0",
|
||||
"@typescript-eslint/parser": "4.33.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.4.0",
|
||||
"@typescript-eslint/parser": "5.4.0",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chalk": "^4.1.2",
|
||||
"eslint": "7.32.0",
|
||||
"eslint": "8.3.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-import-resolver-typescript": "2.5.0",
|
||||
"eslint-plugin-import": "2.24.2",
|
||||
"eslint-plugin-react": "7.26.1",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"eslint-plugin-import": "2.25.3",
|
||||
"eslint-plugin-react": "7.27.1",
|
||||
"eslint-plugin-react-hooks": "4.3.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"marked": "^3.0.8",
|
||||
"mocha": "^9.1.2",
|
||||
"mochawesome": "^6.3.0",
|
||||
"marked": "^4.0.4",
|
||||
"mocha": "^9.1.3",
|
||||
"mochawesome": "^7.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"sass": "^1.43.2",
|
||||
"sinon": "^11.1.2",
|
||||
"sass": "^1.43.4",
|
||||
"sinon": "^12.0.1",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"tslib": "^2.3.1",
|
||||
"ts-node": "^10.2.1",
|
||||
"tsconfig-paths": "^3.11.0",
|
||||
"typescript": "^4.4.3"
|
||||
"ts-node": "^10.4.0",
|
||||
"tsconfig-paths": "^3.12.0",
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"prettier": {
|
||||
"arrowParens": "avoid",
|
||||
|
@ -16,8 +16,8 @@
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"prettier": "^2.4.1",
|
||||
"rollup": "^2.58.0",
|
||||
"rollup-plugin-ts": "^1.4.7",
|
||||
"typedoc": "^0.22.7"
|
||||
"rollup": "^2.60.1",
|
||||
"rollup-plugin-ts": "^2.0.4",
|
||||
"typedoc": "^0.22.9"
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ export default () => ({
|
||||
preventAssignment: true,
|
||||
values: {
|
||||
"process.env.NODE_ENV": '"production"',
|
||||
'require("./adapter").nodeAdapter':
|
||||
'import("./adapter").then(x => x.nodeAdapter)',
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
@ -30,7 +30,7 @@ export class OnePassword {
|
||||
|
||||
constructor({
|
||||
path,
|
||||
adapter = process.browser ? null! : import("./adapter").then(x => x.nodeAdapter),
|
||||
adapter = process.browser ? null! : require("./adapter").nodeAdapter,
|
||||
}: IOptions) {
|
||||
this.#adapter = adapter
|
||||
this.#path = path
|
||||
|
@ -8,7 +8,7 @@ icon: dist/512x512.png
|
||||
directories:
|
||||
output: bundle
|
||||
app: dist
|
||||
buildResources: build
|
||||
buildResources: dist
|
||||
mac:
|
||||
category: public.app-category.productivity
|
||||
target:
|
||||
|
@ -14,24 +14,25 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@emotion/css": "^11.5.0",
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@emotion/react": "^11.6.0",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@rollup/plugin-yaml": "^3.1.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@vitejs/plugin-react": "^1.0.0",
|
||||
"@types/react": "^17.0.36",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@vitejs/plugin-react": "^1.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
"electron": "^15.2.0",
|
||||
"electron-builder": "^22.13.1",
|
||||
"esbuild": "^0.13.6",
|
||||
"electron": "^16.0.1",
|
||||
"electron-builder": "^22.14.5",
|
||||
"esbuild": "^0.13.15",
|
||||
"js-yaml": "^4.1.0",
|
||||
"opvault.js": "*",
|
||||
"path-browserify": "^1.0.1",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-idle-timer": "4.6.4",
|
||||
"react-icons": "^4.3.1",
|
||||
"sass": "^1.43.4",
|
||||
"typescript": "^4.3.2",
|
||||
"vite": "^2.6.4"
|
||||
"typescript": "^4.5.2",
|
||||
"vite": "^2.6.14"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useCallback, useState } from "react"
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import type { Vault, OnePassword } from "opvault.js"
|
||||
import { useIdleTimer } from "react-idle-timer/modern"
|
||||
import { VaultView } from "./pages/Vault"
|
||||
import { VaultPicker } from "./pages/VaultPicker"
|
||||
|
||||
@ -12,6 +13,19 @@ export const App: React.FC = () => {
|
||||
setVault(undefined)
|
||||
}, [vault])
|
||||
|
||||
const { reset, pause } = useIdleTimer({
|
||||
timeout: 60_000,
|
||||
onIdle: onLock,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (vault) {
|
||||
reset()
|
||||
} else {
|
||||
pause()
|
||||
}
|
||||
}, [vault])
|
||||
|
||||
if (!vault) {
|
||||
return (
|
||||
<VaultPicker
|
||||
|
14
packages/web/src/SideEffect.ts
Normal file
14
packages/web/src/SideEffect.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { useEffect, memo } from "react"
|
||||
import { useLocaleContext, useTranslate } from "./i18n"
|
||||
|
||||
export const SideEffect = memo(() => {
|
||||
const { locale } = useLocaleContext()
|
||||
const t = useTranslate()
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.lang = locale
|
||||
document.title = t.label.app_name
|
||||
}, [locale])
|
||||
|
||||
return null
|
||||
})
|
@ -11,10 +11,12 @@ const Container = styled.div`
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 8em;
|
||||
font-size: 6em;
|
||||
text-align: center;
|
||||
padding: 20px 25px;
|
||||
word-break: break-word;
|
||||
min-width: 75vw;
|
||||
z-index: 2;
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { memo } from "react"
|
||||
import { Category } from "opvault.js"
|
||||
import { cx, css } from "@emotion/css"
|
||||
import { BsBank2, BsPeopleFill } from "react-icons/bs"
|
||||
@ -77,14 +78,11 @@ interface CategoryIconProps {
|
||||
category: Category
|
||||
}
|
||||
|
||||
export const CategoryIcon: React.FC<CategoryIconProps> = ({
|
||||
className,
|
||||
category,
|
||||
style,
|
||||
fill,
|
||||
}) => {
|
||||
const Component = getComponent(category)
|
||||
return Component ? (
|
||||
<Component className={cx(reactIconClass, className)} fill={fill} style={style} />
|
||||
) : null
|
||||
}
|
||||
export const CategoryIcon = memo<CategoryIconProps>(
|
||||
({ className, category, style, fill }) => {
|
||||
const Component = getComponent(category)
|
||||
return Component ? (
|
||||
<Component className={cx(reactIconClass, className)} fill={fill} style={style} />
|
||||
) : null
|
||||
}
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ import type { Attachment, AttachmentMetadata, Item, ItemField } from "opvault.js
|
||||
import type { ItemDetails } from "opvault.js/src/types"
|
||||
import { memo, useEffect, useState } from "react"
|
||||
import { useTranslate } from "../i18n"
|
||||
import { ItemNoTitle } from "../styles"
|
||||
import { CategoryIcon } from "./CategoryIcon"
|
||||
import { ItemDates } from "./ItemDates"
|
||||
import {
|
||||
@ -59,7 +60,7 @@ const AttachmentContainer = styled.div`
|
||||
margin: 5px 0;
|
||||
`
|
||||
|
||||
const SectionsView: React.FC<{ sections?: ItemDetails["sections"] }> = ({ sections }) =>
|
||||
const SectionsView = memo<{ sections?: ItemDetails["sections"] }>(({ sections }) =>
|
||||
sections?.length ? (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
{sections
|
||||
@ -74,8 +75,9 @@ const SectionsView: React.FC<{ sections?: ItemDetails["sections"] }> = ({ sectio
|
||||
))}
|
||||
</div>
|
||||
) : null
|
||||
)
|
||||
|
||||
const FieldsView: React.FC<{ fields?: ItemField[] }> = ({ fields }) =>
|
||||
const FieldsView = memo<{ fields?: ItemField[] }>(({ fields }) =>
|
||||
fields?.length ? (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
{fields.map((field, i) => (
|
||||
@ -83,8 +85,9 @@ const FieldsView: React.FC<{ fields?: ItemField[] }> = ({ fields }) =>
|
||||
))}
|
||||
</div>
|
||||
) : null
|
||||
)
|
||||
|
||||
const TagsView: React.FC<{ tags?: string[] }> = ({ tags }) => {
|
||||
const TagsView = memo<{ tags?: string[] }>(({ tags }) => {
|
||||
const t = useTranslate()
|
||||
if (!tags?.length) return null
|
||||
return (
|
||||
@ -97,7 +100,7 @@ const TagsView: React.FC<{ tags?: string[] }> = ({ tags }) => {
|
||||
</div>
|
||||
</ExtraField>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const JSONView = memo<{ item: Item }>(({ item }) => (
|
||||
<details>
|
||||
@ -108,7 +111,7 @@ const JSONView = memo<{ item: Item }>(({ item }) => (
|
||||
</details>
|
||||
))
|
||||
|
||||
export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => {
|
||||
export const ItemView = memo<ItemViewProps>(({ className, item }) => {
|
||||
const t = useTranslate()
|
||||
return (
|
||||
<Container className={className}>
|
||||
@ -117,7 +120,9 @@ export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => {
|
||||
<Header>
|
||||
{item.details.fields == null}
|
||||
<Icon category={item.category} />
|
||||
<ItemTitle>{item.overview.title}</ItemTitle>
|
||||
<ItemTitle>
|
||||
{item.overview.title || <ItemNoTitle>{t.label.no_title}</ItemNoTitle>}
|
||||
</ItemTitle>
|
||||
</Header>
|
||||
|
||||
<JSONView item={item} />
|
||||
@ -161,7 +166,7 @@ export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => {
|
||||
</Inner>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function AttachmentView({ file }: { file: Attachment }) {
|
||||
const [metadata, setMetadata] = useState<AttachmentMetadata>()
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { memo } from "react"
|
||||
import styled from "@emotion/styled"
|
||||
import type { Item } from "opvault.js"
|
||||
import { useTranslate } from "../i18n"
|
||||
@ -9,7 +10,7 @@ const Container = styled.div`
|
||||
opacity: 0.5;
|
||||
`
|
||||
|
||||
export const ItemDates: React.FC<{ item: Item }> = ({ item }) => {
|
||||
export const ItemDates = memo<{ item: Item }>(({ item }) => {
|
||||
const t = useTranslate()
|
||||
return (
|
||||
<Container>
|
||||
@ -21,4 +22,4 @@ export const ItemDates: React.FC<{ item: Item }> = ({ item }) => {
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { memo } from "react"
|
||||
import styled from "@emotion/styled"
|
||||
import type { ItemField, ItemSection } from "opvault.js"
|
||||
import { ErrorBoundary } from "./ErrorBoundary"
|
||||
@ -13,9 +14,9 @@ export const FieldTitle: React.FC = styled.div`
|
||||
margin-bottom: 3px;
|
||||
`
|
||||
|
||||
export const ItemFieldView: React.FC<{
|
||||
export const ItemFieldView = memo<{
|
||||
field: ItemSection.Any
|
||||
}> = ({ field }) => {
|
||||
}>(({ field }) => {
|
||||
if (field.v == null) {
|
||||
return null
|
||||
}
|
||||
@ -28,11 +29,11 @@ export const ItemFieldView: React.FC<{
|
||||
</Container>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export const ItemDetailsFieldView: React.FC<{
|
||||
export const ItemDetailsFieldView = memo<{
|
||||
field: ItemField
|
||||
}> = ({ field }) => {
|
||||
}>(({ field }) => {
|
||||
if (field.value == null) {
|
||||
return null
|
||||
}
|
||||
@ -45,4 +46,4 @@ export const ItemDetailsFieldView: React.FC<{
|
||||
</Container>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -7,7 +7,7 @@ const Container = styled.menu`
|
||||
box-shadow: #0004 0px 1px 4px;
|
||||
left: 99%;
|
||||
margin-block-start: 0;
|
||||
min-width: 150px;
|
||||
min-width: 180px;
|
||||
padding-inline-start: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -9,6 +9,12 @@ import { useItemFieldContextMenu } from "./ItemFieldContextMenu"
|
||||
|
||||
const Container = styled.div``
|
||||
|
||||
function useCopy(text: string) {
|
||||
return useCallback(() => {
|
||||
navigator.clipboard.writeText(text)
|
||||
}, [text])
|
||||
}
|
||||
|
||||
export { Password as PasswordFieldView }
|
||||
|
||||
const Password: React.FC<{
|
||||
@ -19,9 +25,7 @@ const Password: React.FC<{
|
||||
|
||||
const { onRightClick, ContextMenuContainer, Item } = useItemFieldContextMenu()
|
||||
const onToggle = useCallback(() => setShow(x => !x), [])
|
||||
const onCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(field.v)
|
||||
}, [field.v])
|
||||
const onCopy = useCopy(field.v)
|
||||
const onOpenBigText = useCallback(() => {
|
||||
showBigText(true)
|
||||
}, [])
|
||||
@ -67,9 +71,7 @@ const DateView: React.FC<{ field: ItemSection.Date }> = ({ field }) => {
|
||||
|
||||
const TextView: React.FC<{ value: string }> = ({ value }) => {
|
||||
const { onRightClick, ContextMenuContainer, Item } = useItemFieldContextMenu()
|
||||
const onCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(value)
|
||||
}, [value])
|
||||
const onCopy = useCopy(value)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -126,7 +128,7 @@ export const ItemDetailsFieldValue: React.FC<{
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Container>{field.value}</Container>
|
||||
<TextView value={field.value!} />
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { memo } from "react"
|
||||
import styled from "@emotion/styled"
|
||||
import { cx } from "@emotion/css"
|
||||
import type { Item } from "opvault.js"
|
||||
import { CategoryIcon } from "./CategoryIcon"
|
||||
import { useTranslate } from "../i18n"
|
||||
import { ItemNoTitle } from "../styles"
|
||||
|
||||
interface ListProps {
|
||||
items: Item[]
|
||||
@ -36,6 +39,7 @@ const ItemTitle = styled.div`
|
||||
font-weight: 600;
|
||||
margin-bottom: 2px;
|
||||
`
|
||||
|
||||
const ItemDescription = styled.div`
|
||||
font-size: 95%;
|
||||
white-space: nowrap;
|
||||
@ -47,25 +51,30 @@ const Icon = styled(CategoryIcon)`
|
||||
font-size: 1.5em;
|
||||
`
|
||||
|
||||
export const ItemList: React.FC<ListProps> = ({ items, onSelect, selected }) => (
|
||||
<Container>
|
||||
<List>
|
||||
{items.map(item => (
|
||||
<ItemView
|
||||
key={item.uuid}
|
||||
onClick={() => onSelect(item)}
|
||||
className={cx({
|
||||
selected: selected?.uuid === item.uuid,
|
||||
trashed: item.isDeleted,
|
||||
})}
|
||||
>
|
||||
<Icon fill="#FFF" category={item.category} />
|
||||
<div>
|
||||
<ItemTitle>{item.overview.title!}</ItemTitle>
|
||||
<ItemDescription>{item.overview.ainfo || " "}</ItemDescription>
|
||||
</div>
|
||||
</ItemView>
|
||||
))}
|
||||
</List>
|
||||
</Container>
|
||||
)
|
||||
export const ItemList = memo<ListProps>(({ items, onSelect, selected }) => {
|
||||
const t = useTranslate()
|
||||
return (
|
||||
<Container>
|
||||
<List>
|
||||
{items.map(item => (
|
||||
<ItemView
|
||||
key={item.uuid}
|
||||
onClick={() => onSelect(item)}
|
||||
className={cx({
|
||||
selected: selected?.uuid === item.uuid,
|
||||
trashed: item.isDeleted,
|
||||
})}
|
||||
>
|
||||
<Icon fill="#FFF" category={item.category} />
|
||||
<div>
|
||||
<ItemTitle>
|
||||
{item.overview.title || <ItemNoTitle>{t.label.no_title}</ItemNoTitle>}
|
||||
</ItemTitle>
|
||||
<ItemDescription>{item.overview.ainfo || " "}</ItemDescription>
|
||||
</div>
|
||||
</ItemView>
|
||||
))}
|
||||
</List>
|
||||
</Container>
|
||||
)
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import styled from "@emotion/styled"
|
||||
import type { Item } from "opvault.js"
|
||||
import { useMemo } from "react"
|
||||
import { useMemo, memo } from "react"
|
||||
import { parseMonthYear } from "../utils"
|
||||
|
||||
const Container = styled.div`
|
||||
@ -12,7 +12,7 @@ const Container = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
export const ItemWarning: React.FC<{ item: Item }> = ({ item }) => {
|
||||
export const ItemWarning = memo<{ item: Item }>(({ item }) => {
|
||||
const isExpired = useMemo(() => {
|
||||
const fields = item.details.sections?.flatMap(x => x.fields ?? [])
|
||||
if (!fields?.length) return false
|
||||
@ -38,4 +38,4 @@ export const ItemWarning: React.FC<{ item: Item }> = ({ item }) => {
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { memo } from "react"
|
||||
import styled from "@emotion/styled"
|
||||
|
||||
const Container = styled.div`
|
||||
@ -16,8 +17,8 @@ const Title = styled.div`
|
||||
flex-grow: 1;
|
||||
`
|
||||
|
||||
export const TitleBar = () => (
|
||||
export const TitleBar = memo(() => (
|
||||
<Container>
|
||||
<Title>OPVault Viewer</Title>
|
||||
</Container>
|
||||
)
|
||||
))
|
||||
|
@ -1,9 +1,18 @@
|
||||
import { createContext, memo, useContext, useEffect, useMemo, useState } from "react"
|
||||
import {
|
||||
createContext,
|
||||
memo,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react"
|
||||
import texts from "./texts.yml"
|
||||
|
||||
const categories = Object.keys(texts)
|
||||
|
||||
const ALLOWED = new Set(["en", "fr"])
|
||||
const SKIP_ITALIC = new Set(["zh", "ko", "ja"])
|
||||
const LOCALSTORAGE_KEY = "preferred-locale"
|
||||
|
||||
function getLocaleFromStorage() {
|
||||
@ -38,33 +47,33 @@ export const useLocaleContext = () => useContext(LocaleContext)
|
||||
|
||||
export function useTranslate() {
|
||||
const { locale } = useContext(LocaleContext)
|
||||
const t = useMemo(
|
||||
() =>
|
||||
const getter = useCallback(
|
||||
(category: string, key: string) => {
|
||||
const obj = (texts as any)[category]
|
||||
if (
|
||||
process.env.NODE_ENV === "development" &&
|
||||
!Object.prototype.hasOwnProperty.call(obj, key)
|
||||
) {
|
||||
throw new Error(`t.${key} does not exist.`)
|
||||
}
|
||||
return obj[key][locale]
|
||||
},
|
||||
[locale]
|
||||
)
|
||||
|
||||
const t: {
|
||||
[category in keyof typeof texts]: {
|
||||
[key in keyof typeof texts[category]]: string
|
||||
}
|
||||
} = useMemo(
|
||||
(): any =>
|
||||
Object.fromEntries(
|
||||
categories.map(category => [
|
||||
category,
|
||||
new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, p: string) {
|
||||
const obj = (texts as any)[category]
|
||||
if (
|
||||
process.env.NODE_ENV === "development" &&
|
||||
!Object.prototype.hasOwnProperty.call(obj, p)
|
||||
) {
|
||||
throw new Error(`t.${p} does not exist.`)
|
||||
}
|
||||
return obj[p][locale]
|
||||
},
|
||||
}
|
||||
),
|
||||
new Proxy({}, { get: (_, p: string) => getter(category, p) }),
|
||||
])
|
||||
) as {
|
||||
[category in keyof typeof texts]: {
|
||||
[key in keyof typeof texts[category]]: string
|
||||
}
|
||||
},
|
||||
[locale]
|
||||
),
|
||||
[getter]
|
||||
)
|
||||
return t
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
# /* spellchecker: disable */
|
||||
label:
|
||||
app_name:
|
||||
en: OPVault Viewer
|
||||
fr: Lecteur de coffre OPVault
|
||||
|
||||
choose_a_vault:
|
||||
en: Pick a vault
|
||||
fr: Choisir un coffre
|
||||
@ -28,6 +32,23 @@ label:
|
||||
en: Password
|
||||
fr: Mot de passe
|
||||
|
||||
no_title:
|
||||
en: Untitled
|
||||
fr: Sans titre
|
||||
|
||||
options:
|
||||
sort_by_name:
|
||||
en: Sort by Name
|
||||
fr: Trier par nom
|
||||
|
||||
sort_by_created_at:
|
||||
en: Sort by date created
|
||||
fr: Trier par date de création
|
||||
|
||||
sort_by_updated_at:
|
||||
en: Sort by date modified
|
||||
fr: Trier par date de modification
|
||||
|
||||
noun:
|
||||
vault:
|
||||
en: vault
|
||||
|
@ -2,18 +2,21 @@ import React from "react"
|
||||
import { render } from "react-dom"
|
||||
import { App } from "./App"
|
||||
import { LocaleContextProvider } from "./i18n"
|
||||
import { SideEffect } from "./SideEffect"
|
||||
import "./index.scss"
|
||||
|
||||
if (navigator.platform === "MacIntel") {
|
||||
document.documentElement.classList.add("mac")
|
||||
}
|
||||
|
||||
render(
|
||||
const Root: React.FC = () => (
|
||||
<React.StrictMode>
|
||||
{/* <TitleBar /> */}
|
||||
<LocaleContextProvider>
|
||||
<SideEffect />
|
||||
<App />
|
||||
</LocaleContextProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById("app")
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
render(<Root />, document.getElementById("app"))
|
||||
|
3
packages/web/src/modules.d.ts
vendored
Normal file
3
packages/web/src/modules.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare module "react-idle-timer/modern" {
|
||||
export * from "react-idle-timer/dist/modern"
|
||||
}
|
@ -136,9 +136,9 @@ export const VaultView: React.FC<{ vault: Vault; onLock(): void }> = ({
|
||||
value={sortBy}
|
||||
onChange={e => setSortBy(+e.currentTarget.value)}
|
||||
>
|
||||
<option value={SortBy.Name}>Sort by Name</option>
|
||||
<option value={SortBy.CreatedAt}>Sort by Created Time</option>
|
||||
<option value={SortBy.UpdatedAt}>Sort by Updated Time</option>
|
||||
<option value={SortBy.Name}>{t.options.sort_by_name}</option>
|
||||
<option value={SortBy.CreatedAt}>{t.options.sort_by_created_at}</option>
|
||||
<option value={SortBy.UpdatedAt}>{t.options.sort_by_updated_at}</option>
|
||||
</select>
|
||||
</SortContainer>
|
||||
<ItemList items={filtered} onSelect={setItem} selected={item} />
|
||||
|
@ -1,4 +1,15 @@
|
||||
import { css } from "@emotion/css"
|
||||
import styled from "@emotion/styled"
|
||||
|
||||
export const ItemNoTitle = styled.span`
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
[lang^="zh"],
|
||||
[lang="ko"],
|
||||
[lang="ja"] & {
|
||||
font-style: normal;
|
||||
}
|
||||
`
|
||||
|
||||
export const scrollbar = css`
|
||||
&&::-webkit-scrollbar {
|
||||
|
@ -13,6 +13,9 @@ export default defineConfig({
|
||||
},
|
||||
build: {
|
||||
outDir: "dist/web",
|
||||
rollupOptions: {
|
||||
external: ["fs", ""],
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
2678
pnpm-lock.yaml
generated
2678
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user