Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 122 additions & 42 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"d3-fisheye": "^2.0.1",
"gcode-toolpath": "^2.2.0",
"geokdbush": "^1.1.0",
"i18next": "^25.7.3",
"i18next-browser-languagedetector": "^8.2.0",
"javascript-algorithms": "0.0.5",
"kdbush": "^4.0.2",
"konva": "^9.2.0",
Expand All @@ -39,6 +41,7 @@
"react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13",
"react-ga4": "^2.1.0",
"react-i18next": "^16.5.0",
"react-icons": "^5.2.1",
"react-konva": "^18.2.10",
"react-redux": "^9.1.2",
Expand Down
Binary file added public/fonts/SourceHanSerifCN-Bold.ttf
Binary file not shown.
Binary file added public/fonts/SourceHanSerifCN-Regular.ttf
Binary file not shown.
8 changes: 7 additions & 1 deletion src/components/DropdownOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ const DropdownOption = ({

choices = Array.isArray(choices)
? choices.map((choice) => {
return { value: choice, label: choice }
if(typeof choice === 'object') {
return { value: choice.value, label: choice.title }
}
else {
return { value: choice, label: choice }
}

})
: Object.keys(choices).map((key) => {
return { value: key, label: choices[key] }
Expand Down
67 changes: 67 additions & 0 deletions src/components/LanguageSelector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react"
import { useTranslation } from "react-i18next"
import { Dropdown } from "react-bootstrap"

const LanguageSelector = () => {
const { i18n, t } = useTranslation()

const changeLanguage = (lng) => {
if (i18n.language !== lng) {
i18n.changeLanguage(lng)
// 刷新整个页面以更新语言
// window.location.reload()
}
}

const getCurrentLanguageLabel = () => {
switch (i18n.language) {
case "zh":
return t("language.chinese")
case "en":
default:
return t("language.english")
}
}

return (
<Dropdown>
<Dropdown.Toggle
variant="outline-secondary"
size="sm"
id="language-selector"
style={{
color: 'white',
borderColor: 'white',
backgroundColor: 'transparent'
}}
onMouseEnter={(e) => {
e.target.style.backgroundColor = 'white';
e.target.style.color = 'black';
}}
onMouseLeave={(e) => {
e.target.style.backgroundColor = 'transparent';
e.target.style.color = 'white';
}}
>
{getCurrentLanguageLabel()}
</Dropdown.Toggle>

<Dropdown.Menu>
<Dropdown.Item
onClick={() => changeLanguage("en")}
active={i18n.language === "en"}
>
{t("language.english")}
</Dropdown.Item>
<Dropdown.Item
onClick={() => changeLanguage("zh")}
active={i18n.language === "zh"}
>
{t("language.chinese")}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)
}

export default LanguageSelector
19 changes: 11 additions & 8 deletions src/components/QuadrantButtonsOption.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from "react"
import { useTranslation } from "react-i18next"
import Col from "react-bootstrap/Col"
import Row from "react-bootstrap/Row"
import Form from "react-bootstrap/Form"
import ToggleButton from "react-bootstrap/ToggleButton"
import ToggleButtonGroup from "react-bootstrap/ToggleButtonGroup"

const QuadrantButtonsOption = (props) => {
const { t } = useTranslation()
const option = props.options[props.optionKey]
const { data } = props
const value = data[props.optionKey]
Expand Down Expand Up @@ -35,6 +37,7 @@ const QuadrantButtonsOption = (props) => {
type="checkbox"
name="origin"
className="flex-wrap"
style={{ width: '100%' }}
value={value}
onChange={handleChange}
>
Expand All @@ -43,36 +46,36 @@ const QuadrantButtonsOption = (props) => {
value={1}
id="origin-upper-left"
className="px-4"
style={{ borderRadius: 0 }}
style={{ borderRadius: 0, width: '50%' }}
>
upper left
{t('machine.rect.upperLeft')}
</ToggleButton>
<ToggleButton
variant="light"
value={2}
id="origin-upper-right"
className="px-4"
style={{ borderRadius: 0 }}
style={{ borderRadius: 0, width: '50%' }}
>
upper right
{t('machine.rect.upperRight')}
</ToggleButton>
<ToggleButton
variant="light"
value={0}
id="origin-lower-left"
className="px-4"
style={{ borderRadius: 0 }}
style={{ borderRadius: 0, width: '50%' }}
>
lower left
{t('machine.rect.lowerLeft')}
</ToggleButton>
<ToggleButton
variant="light"
value={3}
id="origin-lower-right"
className="px-4"
style={{ borderRadius: 0 }}
style={{ borderRadius: 0, width: '50%' }}
>
lower right
{t('machine.rect.lowerRight')}
</ToggleButton>
</ToggleButtonGroup>
</div>
Expand Down
17 changes: 16 additions & 1 deletion src/components/ToggleButtonOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,20 @@ const ToggleButtonOption = (props) => {
onChange={handleChange}
>
{option.choices.map((choice) => {
return (
if(typeof choice === 'object') {
return (
<ToggleButton
key={choice.value}
id={`${props.optionKey}-${choice.value}`}
variant="light"
value={choice.value}
>
{choice.title}
</ToggleButton>
)
}
else {
return (
<ToggleButton
key={choice}
id={`${props.optionKey}-${choice}`}
Expand All @@ -51,6 +64,8 @@ const ToggleButtonOption = (props) => {
{choice}
</ToggleButton>
)
}

})}
</ToggleButtonGroup>
</Col>
Expand Down
5 changes: 4 additions & 1 deletion src/features/app/About.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react"
import { useTranslation } from "react-i18next"
import Container from "react-bootstrap/Container"
import Row from "react-bootstrap/Row"
import Col from "react-bootstrap/Col"
Expand All @@ -9,6 +10,8 @@ import { SANDIFY_VERSION } from "@/features/app/appSlice"
import "./About.scss"

const About = () => {
const { t } = useTranslation()

return (
<footer className="p-4">
<Container
Expand All @@ -28,7 +31,7 @@ const About = () => {
v{SANDIFY_VERSION}
</div>
<div className="tagline mb-2">
create patterns for robots that draw in sand with ball bearings
{t("about.tagline")}
</div>
<p>
Sandify turns your cold, empty-hearted, emotionless sand tables
Expand Down
22 changes: 13 additions & 9 deletions src/features/app/Header.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from "react"
import { useDispatch } from "react-redux"
import { useTranslation } from "react-i18next"
import Nav from "react-bootstrap/Nav"
import Navbar from "react-bootstrap/Navbar"
import NavDropdown from "react-bootstrap/NavDropdown"
Expand All @@ -8,10 +9,12 @@ import ImageUploader from "@/features/import/ImageUploader"
import LayerUploader from "@/features/import/LayerUploader"
import SandifyDownloader from "@/features/file/SandifyDownloader"
import SandifyUploader from "@/features/file/SandifyUploader"
import LanguageSelector from "@/components/LanguageSelector"
import logo from "./logo.svg"
import "./Header.scss"

const Header = ({ eventKey, setEventKey }) => {
const { t } = useTranslation()
const [showExport, setShowExport] = useState(false)
const [showImportLayer, setShowImportLayer] = useState(0)
const [showImportImage, setShowImportImage] = useState(0)
Expand Down Expand Up @@ -48,36 +51,37 @@ const Header = ({ eventKey, setEventKey }) => {
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<NavDropdown
title="File"
title={t("header.file")}
id="file-dropdown"
>
<NavDropdown.Item onClick={handleNew}>New</NavDropdown.Item>
<NavDropdown.Item onClick={toggleOpen}>Open...</NavDropdown.Item>
<NavDropdown.Item onClick={toggleSave}>Save as...</NavDropdown.Item>
<NavDropdown.Item onClick={handleNew}>{t("header.new")}</NavDropdown.Item>
<NavDropdown.Item onClick={toggleOpen}>{t("header.open")}</NavDropdown.Item>
<NavDropdown.Item onClick={toggleSave}>{t("header.saveAs")}</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item onClick={toggleImportImage}>
Import image...
{t("header.importImage")}
</NavDropdown.Item>
<NavDropdown.Item onClick={toggleImportLayer}>
Import layer...
{t("header.importLayer")}
</NavDropdown.Item>
<NavDropdown.Item onClick={toggleExport}>
Export pattern as...
{t("header.exportPattern")}
</NavDropdown.Item>
</NavDropdown>
<Nav.Link
active={eventKey === "patterns"}
onClick={() => setEventKey("patterns")}
>
Patterns
{t("header.patternsTab")}
</Nav.Link>
<Nav.Link
active={eventKey === "about"}
onClick={() => setEventKey("about")}
>
About
{t("header.aboutTab")}
</Nav.Link>
</Nav>
<LanguageSelector />
</Navbar.Collapse>
<ExportDownloader
showModal={showExport}
Expand Down
8 changes: 5 additions & 3 deletions src/features/app/Sidebar.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useEffect } from "react"
import { useTranslation } from "react-i18next"
import Tab from "react-bootstrap/Tab"
import Tabs from "react-bootstrap/Tabs"
import { useDispatch, useSelector } from "react-redux"
Expand All @@ -10,6 +11,7 @@ import { loadFont, supportedFonts } from "@/features/fonts/fontsSlice"
import { loadImage, selectAllImages } from "@/features/images/imagesSlice"

const Sidebar = () => {
const { t } = useTranslation()
const dispatch = useDispatch()
const layer = useSelector(selectSelectedLayer)
const images = useSelector(selectAllImages)
Expand All @@ -26,23 +28,23 @@ const Sidebar = () => {
<Tabs defaultActiveKey="draw">
<Tab
eventKey="draw"
title="Layers"
title={t("sidebar.layers")}
className="full-page-tab"
>
<LayerManager />
</Tab>

<Tab
eventKey="machines"
title="Machines"
title={t("sidebar.machines")}
className="full-page-tab"
>
<MachineManager />
</Tab>

<Tab
eventKey="stats"
title="Stats"
title={t("sidebar.stats")}
className="full-page-tab"
>
<PreviewStats />
Expand Down
10 changes: 6 additions & 4 deletions src/features/effects/CopyEffect.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import Col from "react-bootstrap/Col"
import Row from "react-bootstrap/Row"
import Form from "react-bootstrap/Form"
import { useDispatch, useSelector } from "react-redux"
import { useTranslation } from "react-i18next"
import { selectSelectedEffect } from "./effectsSlice"
import { addEffect } from "@/features/layers/layersSlice"

const CopyEffect = ({ toggleModal, showModal }) => {
const { t } = useTranslation()
const dispatch = useDispatch()
const selectedEffect = useSelector(selectSelectedEffect)
const namedInputRef = useRef(null)
Expand Down Expand Up @@ -53,13 +55,13 @@ const CopyEffect = ({ toggleModal, showModal }) => {
onEntered={handleInitialFocus}
>
<Modal.Header closeButton>
<Modal.Title>Copy {selectedEffect?.name || ""}</Modal.Title>
<Modal.Title>{t('copyEffect.title', { name: selectedEffect?.name || "" })}</Modal.Title>
</Modal.Header>

<Form onSubmit={handleCopyEffect}>
<Modal.Body>
<Row className="align-items-center">
<Col sm={5}>Name</Col>
<Col sm={5}>{t('copyEffect.name')}</Col>
<Col sm={7}>
<Form.Control
ref={namedInputRef}
Expand All @@ -77,14 +79,14 @@ const CopyEffect = ({ toggleModal, showModal }) => {
variant="light"
onClick={toggleModal}
>
Cancel
{t('common.cancel')}
</Button>
<Button
id="copy-layer-copy"
variant="primary"
type="submit"
>
Copy
{t('common.copy')}
</Button>
</Modal.Footer>
</Form>
Expand Down
5 changes: 4 additions & 1 deletion src/features/effects/EffectEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import ModelOption from "@/components/ModelOption"
import { updateEffect } from "./effectsSlice"
import EffectLayer from "./EffectLayer"
import { selectSelectedEffect } from "./effectsSlice"
import { useTranslation } from "react-i18next"


const EffectEditor = ({ id }) => {
const { t } = useTranslation()
const dispatch = useDispatch()
const effect = useSelector(selectSelectedEffect)
const type = effect?.type || "mask" // guard zombie child
Expand Down Expand Up @@ -69,7 +72,7 @@ const EffectEditor = ({ id }) => {
{renderedModelOptions}
{model.canTransform(effect) && (
<Row className="align-items-center mt-1 mb-1">
<Col sm={3}>Transform</Col>
<Col sm={3}>{t('layer.transform')}</Col>
<Col sm={9}>
<div className="d-flex">
<div className="d-flex flex-column">
Expand Down
Loading