Version: " - + version - + "
" - + "A desktop tool for viewing and modifying Unity packages.
" - + "GitHub: https://github.com/habedi/uview
" - + ""; - - java.net.URL iconUrl = App.class.getResource("/logo.svg"); - Icon aboutIcon = (iconUrl != null) ? new FlatSVGIcon(iconUrl).derive(64, 64) : null; - - JLabel messageLabel = new JLabel(message); - messageLabel.addMouseListener( - new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - try { - Desktop.getDesktop().browse(new java.net.URI("https://github.com/habedi/uview")); - } catch (Exception ex) { - // Ignore - } - } - }); - - JOptionPane.showMessageDialog( - this, messageLabel, title, JOptionPane.INFORMATION_MESSAGE, aboutIcon); - } - - private String getAppVersion() { - try (InputStream is = - App.class.getResourceAsStream( - "/META-INF/maven/io.github.pixelclover/uview/pom.properties")) { - if (is == null) return "N/A"; - Properties props = new Properties(); - props.load(is); - return props.getProperty("version", "N/A"); - } catch (IOException e) { - return "N/A"; - } + AboutDialog aboutDialog = new AboutDialog(this); + aboutDialog.setVisible(true); } private void populateRecentFilesMenu() { @@ -467,7 +524,9 @@ private PackageViewPanel getCurrentPanel() { private void saveFile() { PackageViewPanel currentPanel = getCurrentPanel(); - if (currentPanel == null) return; + if (currentPanel == null) { + return; + } if (currentPanel.getPackageFile() == null) { saveFileAs(); } else { @@ -477,7 +536,9 @@ private void saveFile() { private void saveFileAs() { PackageViewPanel currentPanel = getCurrentPanel(); - if (currentPanel == null) return; + if (currentPanel == null) { + return; + } JFileChooser chooser = createFileChooser("Save Unity Package As..."); chooser.setFileFilter(new FileNameExtensionFilter("Unity Package", "unitypackage")); @@ -560,7 +621,9 @@ protected void done() { private void extractAll() { PackageViewPanel currentPanel = getCurrentPanel(); - if (currentPanel == null) return; + if (currentPanel == null) { + return; + } JFileChooser chooser = createFileChooser("Select Directory to Extract All Assets"); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); @@ -611,11 +674,14 @@ void updateState() { boolean hasPanel = currentPanel != null; saveMenuItem.setEnabled(hasPanel && currentPanel.getPackageManager().isModified()); + saveButton.setEnabled(hasPanel && currentPanel.getPackageManager().isModified()); saveAsMenuItem.setEnabled(hasPanel); closeMenuItem.setEnabled(hasPanel); extractAllMenuItem.setEnabled(hasPanel); + extractAllButton.setEnabled(hasPanel); if (hasPanel) { + cardLayout.show(contentPanel, TABBED_PANE); int selectedIndex = tabbedPane.getSelectedIndex(); tabbedPane.setTitleAt(selectedIndex, currentPanel.getTabTitle()); @@ -633,6 +699,7 @@ void updateState() { packageSizeLabel.setText(""); } } else { + cardLayout.show(contentPanel, WELCOME_PANEL); setTitle("UView"); statusLabel.setText("Ready"); fileCountLabel.setText(""); diff --git a/src/main/java/io/github/pixelclover/uview/gui/MetaEditorFrame.java b/src/main/java/io/github/pixelclover/uview/gui/MetaEditorFrame.java index dac3c8a..2acec41 100644 --- a/src/main/java/io/github/pixelclover/uview/gui/MetaEditorFrame.java +++ b/src/main/java/io/github/pixelclover/uview/gui/MetaEditorFrame.java @@ -2,9 +2,13 @@ import io.github.pixelclover.uview.core.PackageManager; import io.github.pixelclover.uview.model.UnityAsset; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.FlowLayout; import java.nio.charset.StandardCharsets; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rsyntaxtextarea.Theme; @@ -46,28 +50,28 @@ public MetaEditorFrame( } private RSyntaxTextArea createTextArea() { - RSyntaxTextArea rSyntaxTextArea = new RSyntaxTextArea(); - rSyntaxTextArea.setEditable(true); + RSyntaxTextArea rsyntaxTextArea = new RSyntaxTextArea(); + rsyntaxTextArea.setEditable(true); if (asset.metaContent() != null) { - rSyntaxTextArea.setText(new String(asset.metaContent(), StandardCharsets.UTF_8)); + rsyntaxTextArea.setText(new String(asset.metaContent(), StandardCharsets.UTF_8)); } else { String defaultMeta = String.format("fileFormatVersion: 2\nguid: %s\n", asset.guid()); - rSyntaxTextArea.setText(defaultMeta); + rsyntaxTextArea.setText(defaultMeta); } - rSyntaxTextArea.setCaretPosition(0); + rsyntaxTextArea.setCaretPosition(0); // Apply syntax highlighting and theme - rSyntaxTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_YAML); + rsyntaxTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_YAML); try { Theme theme = Theme.load( getClass().getResourceAsStream("/org/fife/ui/rsyntaxtextarea/themes/dark.xml")); - theme.apply(rSyntaxTextArea); + theme.apply(rsyntaxTextArea); } catch (Exception e) { // Ignore, fallback to default theme } - return rSyntaxTextArea; + return rsyntaxTextArea; } private JPanel createButtonPanel() { diff --git a/src/main/java/io/github/pixelclover/uview/gui/PackageViewPanel.java b/src/main/java/io/github/pixelclover/uview/gui/PackageViewPanel.java index 6c59b38..a68b9a1 100644 --- a/src/main/java/io/github/pixelclover/uview/gui/PackageViewPanel.java +++ b/src/main/java/io/github/pixelclover/uview/gui/PackageViewPanel.java @@ -6,14 +6,30 @@ import io.github.pixelclover.uview.gui.tree.TreeEntry; import io.github.pixelclover.uview.io.PackageIO; import io.github.pixelclover.uview.model.UnityAsset; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Dimension; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.nio.file.Path; import java.util.Collection; import java.util.List; -import javax.swing.*; +import javax.swing.BorderFactory; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import javax.swing.Timer; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.tree.DefaultMutableTreeNode; @@ -140,7 +156,6 @@ private void filterTree() { private JPopupMenu createPopupMenu() { JPopupMenu popup = new JPopupMenu(); - TreePath selectionPath = tree.getSelectionPath(); JMenuItem viewMenuItem = new JMenuItem("View"); viewMenuItem.addActionListener(e -> handleDoubleClick()); @@ -172,6 +187,7 @@ private JPopupMenu createPopupMenu() { removeMenuItem.setEnabled(false); extractSelectedMenuItem.setEnabled(false); + TreePath selectionPath = tree.getSelectionPath(); if (selectionPath != null) { DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) selectionPath.getLastPathComponent(); diff --git a/src/main/java/io/github/pixelclover/uview/gui/PdfViewerPanel.java b/src/main/java/io/github/pixelclover/uview/gui/PdfViewerPanel.java index c7a9e5f..838ad16 100644 --- a/src/main/java/io/github/pixelclover/uview/gui/PdfViewerPanel.java +++ b/src/main/java/io/github/pixelclover/uview/gui/PdfViewerPanel.java @@ -1,11 +1,20 @@ package io.github.pixelclover.uview.gui; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.FlowLayout; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.IOException; -import javax.swing.*; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; diff --git a/src/main/java/io/github/pixelclover/uview/gui/SyntaxTextPanel.java b/src/main/java/io/github/pixelclover/uview/gui/SyntaxTextPanel.java index 86aa218..27ed880 100644 --- a/src/main/java/io/github/pixelclover/uview/gui/SyntaxTextPanel.java +++ b/src/main/java/io/github/pixelclover/uview/gui/SyntaxTextPanel.java @@ -1,13 +1,19 @@ package io.github.pixelclover.uview.gui; import io.github.pixelclover.uview.model.UnityAsset; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import javax.swing.*; +import javax.swing.JPanel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import org.fife.ui.rsyntaxtextarea.*; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.Style; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.fife.ui.rsyntaxtextarea.SyntaxScheme; +import org.fife.ui.rsyntaxtextarea.Theme; +import org.fife.ui.rsyntaxtextarea.Token; import org.fife.ui.rtextarea.RTextScrollPane; /** diff --git a/src/main/java/io/github/pixelclover/uview/gui/VideoPlayerPanel.java b/src/main/java/io/github/pixelclover/uview/gui/VideoPlayerPanel.java new file mode 100644 index 0000000..f7ae9a1 --- /dev/null +++ b/src/main/java/io/github/pixelclover/uview/gui/VideoPlayerPanel.java @@ -0,0 +1,142 @@ +package io.github.pixelclover.uview.gui; + +import io.github.pixelclover.uview.model.UnityAsset; +import java.awt.BorderLayout; +import java.nio.file.Path; +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.Slider; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; +import javafx.scene.media.MediaView; +import javafx.util.Duration; +import javax.swing.JPanel; + +/** A panel for playing video files using JavaFX media components. */ +public class VideoPlayerPanel extends JPanel { + + private final JFXPanel jfxPanel = new JFXPanel(); + private MediaPlayer mediaPlayer; + + /** + * Constructs a new video player panel. + * + * @param asset The Unity asset representing the video. + * @param assetPath The path to the video file on disk. + */ + public VideoPlayerPanel(UnityAsset asset, Path assetPath) { + setLayout(new BorderLayout()); + add(jfxPanel, BorderLayout.CENTER); + + Platform.setImplicitExit(false); + Platform.runLater( + () -> { + try { + Media media = new Media(assetPath.toUri().toString()); + mediaPlayer = new MediaPlayer(media); + mediaPlayer.setAutoPlay(true); + + MediaView mediaView = new MediaView(mediaPlayer); + mediaView.setPreserveRatio(true); + + BorderPane root = new BorderPane(); + root.setCenter(mediaView); + root.setBottom(createControls()); + + Scene scene = new Scene(root); + mediaView.fitWidthProperty().bind(scene.widthProperty()); + mediaView.fitHeightProperty().bind(scene.heightProperty().subtract(50)); + jfxPanel.setScene(scene); + } catch (Exception e) { + // Handle exceptions, e.g., by showing an error message + } + }); + } + + private HBox createControls() { + HBox controls = new HBox(10); + controls.setAlignment(Pos.CENTER); + controls.setPadding(new Insets(10)); + + Button playPauseButton = new Button("Pause"); + playPauseButton.setOnAction( + e -> { + if (mediaPlayer.getStatus() == MediaPlayer.Status.PLAYING) { + mediaPlayer.pause(); + playPauseButton.setText("Play"); + } else { + mediaPlayer.play(); + playPauseButton.setText("Pause"); + } + }); + + Label timeLabel = new Label(); + Slider timeSlider = new Slider(); + HBox.setHgrow(timeSlider, Priority.ALWAYS); + + Slider volumeSlider = new Slider(0, 1, 1); + volumeSlider.setPrefWidth(100); + volumeSlider + .valueProperty() + .addListener((obs, oldVal, newVal) -> mediaPlayer.setVolume(newVal.doubleValue())); + + controls.getChildren().addAll(playPauseButton, timeSlider, timeLabel, volumeSlider); + + mediaPlayer + .currentTimeProperty() + .addListener( + (obs, oldTime, newTime) -> { + if (!timeSlider.isValueChanging()) { + timeSlider.setValue(newTime.toSeconds()); + } + timeLabel.setText( + formatDuration(newTime) + " / " + formatDuration(mediaPlayer.getTotalDuration())); + }); + + mediaPlayer.setOnReady( + () -> { + timeSlider.setMax(mediaPlayer.getTotalDuration().toSeconds()); + timeLabel.setText("00:00 / " + formatDuration(mediaPlayer.getTotalDuration())); + }); + + timeSlider + .valueProperty() + .addListener( + (obs, oldValue, newValue) -> { + if (timeSlider.isValueChanging()) { + mediaPlayer.seek(Duration.seconds(newValue.doubleValue())); + } + }); + + return controls; + } + + private String formatDuration(Duration duration) { + if (duration == null || duration.isUnknown()) { + return "00:00"; + } + long seconds = (long) duration.toSeconds(); + long minutes = seconds / 60; + long secs = seconds % 60; + return String.format("%02d:%02d", minutes, secs); + } + + /** Stops the media player and releases resources. */ + public void stop() { + if (mediaPlayer != null) { + Platform.runLater( + () -> { + mediaPlayer.stop(); + mediaPlayer.dispose(); + }); + } + } +} diff --git a/src/main/java/io/github/pixelclover/uview/gui/WelcomePanel.java b/src/main/java/io/github/pixelclover/uview/gui/WelcomePanel.java new file mode 100644 index 0000000..2cf06a6 --- /dev/null +++ b/src/main/java/io/github/pixelclover/uview/gui/WelcomePanel.java @@ -0,0 +1,65 @@ +package io.github.pixelclover.uview.gui; + +import com.formdev.flatlaf.extras.FlatSVGIcon; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +/** + * A panel to be displayed when no tabs are open, welcoming the user and providing quick actions. + */ +public class WelcomePanel extends JPanel { + + /** + * Constructs a new welcome panel. + * + * @param newPackageAction The action to run when the "New Package" button is clicked. + * @param openFileAction The action to run when the "Open..." button is clicked. + */ + public WelcomePanel(Runnable newPackageAction, Runnable openFileAction) { + setLayout(new BorderLayout()); + setBorder(new EmptyBorder(20, 20, 20, 20)); + + // Logo and Title + JPanel headerPanel = new JPanel(); + headerPanel.setLayout(new BoxLayout(headerPanel, BoxLayout.Y_AXIS)); + headerPanel.setAlignmentX(CENTER_ALIGNMENT); + + FlatSVGIcon logo = new FlatSVGIcon("logo.svg", 128, 128); + JLabel logoLabel = new JLabel(logo); + logoLabel.setAlignmentX(CENTER_ALIGNMENT); + headerPanel.add(logoLabel); + + headerPanel.add(Box.createRigidArea(new Dimension(0, 20))); + + JLabel titleLabel = new JLabel("Welcome to UView"); + titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 24)); + titleLabel.setAlignmentX(CENTER_ALIGNMENT); + headerPanel.add(titleLabel); + + JLabel subtitleLabel = new JLabel("A tool for viewing and modifying Unity packages."); + subtitleLabel.setAlignmentX(CENTER_ALIGNMENT); + headerPanel.add(subtitleLabel); + + add(headerPanel, BorderLayout.CENTER); + + // Action Buttons + JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10)); + JButton newButton = new JButton("New Package"); + newButton.addActionListener(e -> newPackageAction.run()); + actionPanel.add(newButton); + + JButton openButton = new JButton("Open..."); + openButton.addActionListener(e -> openFileAction.run()); + actionPanel.add(openButton); + + add(actionPanel, BorderLayout.SOUTH); + } +} diff --git a/src/main/java/io/github/pixelclover/uview/io/PackageIO.java b/src/main/java/io/github/pixelclover/uview/io/PackageIO.java index f377f35..24e59f9 100644 --- a/src/main/java/io/github/pixelclover/uview/io/PackageIO.java +++ b/src/main/java/io/github/pixelclover/uview/io/PackageIO.java @@ -2,7 +2,11 @@ import io.github.pixelclover.uview.model.UnityAsset; import io.github.pixelclover.uview.model.UnityPackage; -import java.io.*; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; diff --git a/src/main/resources/fonts/jetbrains/OFL.txt b/src/main/resources/fonts/jetbrains/OFL.txt index 8bee414..23a3dca 100644 --- a/src/main/resources/fonts/jetbrains/OFL.txt +++ b/src/main/resources/fonts/jetbrains/OFL.txt @@ -18,7 +18,7 @@ with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, +fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The diff --git a/src/main/resources/icons/tabler_v1/device-floppy.svg b/src/main/resources/icons/tabler_v1/device-floppy.svg new file mode 100644 index 0000000..33d6f87 --- /dev/null +++ b/src/main/resources/icons/tabler_v1/device-floppy.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/icons/tabler_v1/file-plus.svg b/src/main/resources/icons/tabler_v1/file-plus.svg new file mode 100644 index 0000000..8005230 --- /dev/null +++ b/src/main/resources/icons/tabler_v1/file-plus.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/icons/tabler_v1/folder-open.svg b/src/main/resources/icons/tabler_v1/folder-open.svg new file mode 100644 index 0000000..6d6cd7f --- /dev/null +++ b/src/main/resources/icons/tabler_v1/folder-open.svg @@ -0,0 +1 @@ + diff --git a/src/test/java/io/github/pixelclover/uview/core/PackageManagerTest.java b/src/test/java/io/github/pixelclover/uview/core/PackageManagerTest.java index b53ebc4..78427c0 100644 --- a/src/test/java/io/github/pixelclover/uview/core/PackageManagerTest.java +++ b/src/test/java/io/github/pixelclover/uview/core/PackageManagerTest.java @@ -116,4 +116,69 @@ void getFilteredAssetsReturnsCorrectSubset() throws IOException { assertEquals( "Assets/Scripts/Player/PlayerController.cs", csResults.iterator().next().assetPath()); } + + @Test + void addAssetThrowsExceptionForLargeFile() throws IOException { + Path largeFile = tempDir.resolve("large-file.bin"); + Files.write(largeFile, new byte[20]); // 20 bytes + + // Create a new PackageManager with a tiny size limit for this test + PackageManager sizeLimitedManager = new PackageManager(new PackageIO(), 10); // 10 byte limit + + IOException e = + assertThrows( + IOException.class, + () -> sizeLimitedManager.addAsset(largeFile, "Assets/large-file.bin"), + "Should throw IOException for file exceeding max size."); + assertTrue( + e.getMessage().startsWith("File is too large"), + "Exception message should indicate file is too large."); + } + + @Test + void updateAssetContentUpdatesTheAsset() throws IOException { + String assetPath = "Assets/MyFile.txt"; + packageManager.addAsset(sourceFile, assetPath); + assertEquals("test", new String(packageManager.getAssets().iterator().next().content())); + + byte[] newContent = "updated content".getBytes(StandardCharsets.UTF_8); + packageManager.updateAssetContent(assetPath, newContent); + + assertTrue(packageManager.isModified()); + assertEquals(1, packageManager.getAssets().size()); + assertEquals( + "updated content", new String(packageManager.getAssets().iterator().next().content())); + } + + @Test + void updateAssetMetaMarksPackageAsModified() throws IOException { + String assetPath = "Assets/MyFile.txt"; + packageManager.addAsset(sourceFile, assetPath); + packageManager.savePackage(new File("dummy.unitypackage")); // Reset modified flag + assertFalse(packageManager.isModified()); + + byte[] newMetaContent = "new meta".getBytes(StandardCharsets.UTF_8); + packageManager.updateAssetMeta(assetPath, newMetaContent); + + assertTrue(packageManager.isModified()); + assertEquals( + "new meta", new String(packageManager.getAssets().iterator().next().metaContent())); + } + + @Test + void removeDirectoryMarksPackageAsModifiedAndRemovesAssets() throws IOException { + packageManager.addAsset(sourceFile, "Assets/MyDir/File1.txt"); + packageManager.addAsset(sourceFile2, "Assets/MyDir/SubDir/File2.txt"); + packageManager.addAsset(sourceFile, "Assets/Other/File3.txt"); + packageManager.savePackage(new File("dummy.unitypackage")); // Reset modified flag + assertFalse(packageManager.isModified()); + assertEquals(3, packageManager.getAssets().size()); + + packageManager.removeDirectory("Assets/MyDir"); + + assertTrue(packageManager.isModified()); + assertEquals(1, packageManager.getAssets().size()); + assertEquals( + "Assets/Other/File3.txt", packageManager.getAssets().iterator().next().assetPath()); + } } diff --git a/src/test/java/io/github/pixelclover/uview/gui/FontManagerTest.java b/src/test/java/io/github/pixelclover/uview/gui/FontManagerTest.java new file mode 100644 index 0000000..a1f2add --- /dev/null +++ b/src/test/java/io/github/pixelclover/uview/gui/FontManagerTest.java @@ -0,0 +1,21 @@ +package io.github.pixelclover.uview.gui; + +import static org.junit.jupiter.api.Assertions.*; + +import java.awt.GraphicsEnvironment; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +class FontManagerTest { + + @Test + void loadAndRegisterFontsDoesNotThrowException() { + // This test can only run in a headful environment. + Assumptions.assumeFalse(GraphicsEnvironment.isHeadless()); + + // The test passes if this method executes without throwing any exceptions. + assertDoesNotThrow( + FontManager::loadAndRegisterFonts, + "FontManager.loadAndRegisterFonts() should not throw any exceptions."); + } +} diff --git a/test_assets/test_image.png b/test_assets/test_image.png new file mode 100644 index 0000000..065dbcd --- /dev/null +++ b/test_assets/test_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a5fbe5cd6c8d733c0424bdbcd2f71d4261db94adaf96b1d3bd166091930d301 +size 89 diff --git a/test_assets/test_script.cs b/test_assets/test_script.cs new file mode 100644 index 0000000..ce06b7c --- /dev/null +++ b/test_assets/test_script.cs @@ -0,0 +1,4 @@ +// This is a dummy C# script for testing. +public class TestScript { + // Hello, World! +}