├── src
├── styles
│ ├── options.css
│ ├── help.css
│ ├── main.css
│ ├── appbar.css
│ ├── list-field.css
│ ├── output-box.css
│ └── list-container.css
├── js
│ ├── components
│ │ ├── Transition.js
│ │ ├── DragHandle.js
│ │ ├── options
│ │ │ ├── TagMenu.js
│ │ │ ├── LocalVarPrefixField.js
│ │ │ ├── OptionsButton.js
│ │ │ ├── LegacyToggle.js
│ │ │ ├── FuncTagSelector.js
│ │ │ ├── DescTagSelector.js
│ │ │ ├── ArgTagSelector.js
│ │ │ └── OptionsDialog.js
│ │ ├── arguments
│ │ │ ├── AddArgumentButton.js
│ │ │ ├── ArgumentContainer.js
│ │ │ ├── ArgumentField.js
│ │ │ ├── ArgumentSortable.js
│ │ │ └── ArgumentDialog.js
│ │ ├── localvars
│ │ │ ├── AddLocalVarButton.js
│ │ │ ├── LocalVarContainer.js
│ │ │ ├── LocalVarField.js
│ │ │ └── LocalVarSortable.js
│ │ ├── ScriptNameField.js
│ │ ├── DescriptionField.js
│ │ ├── help
│ │ │ ├── HelpButton.js
│ │ │ └── HelpDialog.js
│ │ ├── OutputBox.js
│ │ ├── TitleBar.js
│ │ ├── App.js
│ │ └── CopyScriptButton.js
│ ├── actions
│ │ ├── id.js
│ │ ├── arguments.js
│ │ ├── localVars.js
│ │ └── options.js
│ ├── index.js
│ ├── helpers
│ │ ├── initialState.js
│ │ ├── EventTypes.js
│ │ ├── ColourTheme.js
│ │ └── generateScript.js
│ ├── store
│ │ ├── localStorage.js
│ │ └── index.js
│ ├── electron-index.js
│ └── reducers
│ │ └── index.js
└── index.html
├── docs
├── gmlsw-1.png
└── gmlsw-2.png
├── electron
├── package.json
└── main.js
├── .babelrc
├── GMLScriptWizard.desktop
├── .gitignore
├── webpack.prod.js
├── webpack.dev.js
├── .eslintrc
├── README.md
├── webpack.common.js
├── CHANGELOG.md
├── LICENSE
└── package.json
/src/styles/options.css:
--------------------------------------------------------------------------------
1 | .options-root {
2 | padding: 72px 8px 0 8px;
3 | }
--------------------------------------------------------------------------------
/docs/gmlsw-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mstop4/gml-script-wizard/HEAD/docs/gmlsw-1.png
--------------------------------------------------------------------------------
/docs/gmlsw-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mstop4/gml-script-wizard/HEAD/docs/gmlsw-2.png
--------------------------------------------------------------------------------
/electron/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gml-script-wizard",
3 | "version": "0.9.3",
4 | "main": "main.js"
5 | }
--------------------------------------------------------------------------------
/src/styles/help.css:
--------------------------------------------------------------------------------
1 | .help-link:link, .help-link:visited, .help-link:hover, .help-link:active {
2 | color: #039d5b;
3 | text-decoration: none;
4 | }
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "env",
4 | "react"
5 | ],
6 |
7 | "plugins": [
8 | "transform-object-rest-spread"
9 | ]
10 | }
--------------------------------------------------------------------------------
/src/styles/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #181818;
3 | padding: 72px 8px 0 8px;
4 | }
5 |
6 | ul {
7 | list-style: none;
8 | padding-left: 0;
9 | }
--------------------------------------------------------------------------------
/GMLScriptWizard.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Encoding=UTF-8
3 | Version=1.0
4 | Type=Application
5 | Terminal=false
6 | Exec=/path/to/executable/GMLScriptWizard
7 | Name=GML Script Wizard
--------------------------------------------------------------------------------
/src/js/components/Transition.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Slide from 'material-ui/transitions/Slide'
3 |
4 | const Transition = (props) => {
5 | return
6 | }
7 |
8 | export default Transition
--------------------------------------------------------------------------------
/src/js/components/DragHandle.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Icon from 'material-ui/Icon'
3 | import { SortableHandle } from 'react-sortable-hoc'
4 |
5 | const DragHandle = SortableHandle(() => reorder )
6 |
7 | export default DragHandle
--------------------------------------------------------------------------------
/src/styles/appbar.css:
--------------------------------------------------------------------------------
1 | .appbar-root {
2 | display: flex;
3 | flex-grow: 1;
4 | flex-basis: 0;
5 | justify-content: space-between;
6 | align-items: center;
7 | }
8 |
9 | .appbar-branding {
10 | flex-grow: 1;
11 | flex-basis: 0;
12 | }
13 |
14 | .appbar-buttons {
15 | }
16 |
--------------------------------------------------------------------------------
/src/js/actions/id.js:
--------------------------------------------------------------------------------
1 | import { NAME_CHANGE, DESC_CHANGE } from '../helpers/eventTypes'
2 |
3 | export const scriptNameChange = (value) => ({
4 | type: NAME_CHANGE,
5 | payload: {value: value}
6 | })
7 |
8 | export const descriptionChange = (value) => ({
9 | type: DESC_CHANGE,
10 | payload: {value: value}
11 | })
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | dist/*
3 | electron/js/
4 | electron/*.html
5 | .DS_Store
6 | yarn-error.log
7 | *.stackdump
8 |
9 | GMLScriptWizard-win32-x64/
10 | GMLScriptWizard-darwin-x64/
11 | GMLScriptWizard-linux-x64/
12 |
13 | win32-x64-template/
14 | darwin-x64-template/
15 | linux-x64-template/
16 |
17 | *.zip
18 | *.tar.gz
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { Provider } from 'react-redux'
4 | import store from './store/index'
5 | import App from './components/App'
6 |
7 | render (
8 |
9 |
10 | ,
11 | document.getElementById('main-app')
12 | )
--------------------------------------------------------------------------------
/src/js/helpers/initialState.js:
--------------------------------------------------------------------------------
1 | export const initialState = {
2 | options: {
3 | legacyMode: false,
4 | localVarPrefix: '_',
5 | functionTag: '@function',
6 | descriptionTag: '@description',
7 | argumentTag: '@argument'
8 | },
9 | scriptName: '',
10 | description: '',
11 | outputValue: '',
12 | args: [],
13 | localVars: [],
14 | }
--------------------------------------------------------------------------------
/src/styles/list-field.css:
--------------------------------------------------------------------------------
1 | .field-root {
2 | display: flex;
3 | justify-content: space-between;
4 | align-items: center;
5 | }
6 |
7 | .field-button {
8 | padding: 5px;
9 | }
10 |
11 | .field-textfield {
12 | padding: 5px;
13 | flex-grow: 1;
14 | flex-basis: 0;
15 | }
16 |
17 | .field-drag-handle {
18 | color: #fff;
19 | padding: 8px 16px;
20 | }
--------------------------------------------------------------------------------
/src/styles/output-box.css:
--------------------------------------------------------------------------------
1 | #generated-script {
2 | color: #fff;
3 | font-size: 14px;
4 | font-family: 'Droid Sans Mono', monospace;
5 | white-space: pre-line;
6 | }
7 |
8 | .output-root {
9 | display: flex;
10 | justify-content: space-between;
11 | align-items: center;
12 | }
13 |
14 | .output-title {
15 | flex-grow: 1;
16 | flex-basis: 0;
17 | }
--------------------------------------------------------------------------------
/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const merge = require('webpack-merge')
3 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
4 | const common = require('./webpack.common.js')
5 |
6 | module.exports = merge( common, {
7 | plugins: [
8 | new UglifyJSPlugin(),
9 | new webpack.DefinePlugin({
10 | 'process.env': {
11 | 'NODE_ENV': JSON.stringify('production')
12 | }
13 | })
14 | ]
15 | })
--------------------------------------------------------------------------------
/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const merge = require('webpack-merge')
3 | const common = require('./webpack.common.js')
4 |
5 | module.exports = merge( common, {
6 | devtool: 'inline-source-map',
7 |
8 | devServer: {
9 | contentBase: './dist'
10 | },
11 |
12 | plugins: [
13 | new webpack.DefinePlugin({
14 | 'process.env': {
15 | 'NODE_ENV': JSON.stringify('development')
16 | }
17 | })
18 | ]
19 | })
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | GML Script Wizard
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/styles/list-container.css:
--------------------------------------------------------------------------------
1 | .container-root {
2 | overflow-x: hidden;
3 | }
4 |
5 | .container-header {
6 | display: flex;
7 | justify-content: space-between;
8 | align-items: center;
9 | }
10 |
11 | .container-title {
12 | flex-grow: 1;
13 | flex-basis: 0;
14 | padding: 5px;
15 | }
16 |
17 | .container-add {
18 | padding: 1px;
19 | }
20 |
21 | .container-list {
22 | height: 100vh;
23 | height: -webkit-calc(100vh - 152px);
24 | height: -moz-calc(100vh - 152px);
25 | height: calc(100vh - 152px);
26 | }
--------------------------------------------------------------------------------
/src/js/store/localStorage.js:
--------------------------------------------------------------------------------
1 | export const loadState = () => {
2 | try {
3 | const serialState = localStorage.getItem('state')
4 | if (serialState === null) {
5 | return undefined
6 | }
7 | return JSON.parse(serialState)
8 | } catch (err) {
9 | return undefined
10 | }
11 | }
12 |
13 | export const saveState = (state) => {
14 | try {
15 | const serializedState = JSON.stringify(state)
16 | localStorage.setItem('state', serializedState)
17 | } catch (err) {
18 | // Do nothing
19 | }
20 | }
--------------------------------------------------------------------------------
/src/js/helpers/EventTypes.js:
--------------------------------------------------------------------------------
1 | export const NAME_CHANGE = 'NAME_CHANGE'
2 | export const DESC_CHANGE = 'DESC_CHANGE'
3 |
4 | export const ARG_CHANGE = 'ARG_CHANGE'
5 | export const ARG_SORT = 'ARG_SORT'
6 | export const ARG_ADD = 'ARG_ADD'
7 | export const ARG_REMOVE = 'ARG_REMOVE'
8 |
9 | export const LVAR_CHANGE = 'LVAR_CHANGE'
10 | export const LVAR_SORT = 'LVAR_SORT'
11 | export const LVAR_ADD = 'LVAR_ADD'
12 | export const LVAR_REMOVE = 'LVAR_REMOVE'
13 |
14 | export const OPT_LEGACY = 'OPT_LEGACY'
15 | export const OPT_CHANGE = 'OPT_CHANGE'
--------------------------------------------------------------------------------
/src/js/actions/arguments.js:
--------------------------------------------------------------------------------
1 | import { ARG_CHANGE, ARG_SORT, ARG_ADD, ARG_REMOVE } from '../helpers/eventTypes'
2 |
3 | export const argumentChange = (id, key, value) => ({
4 | type: ARG_CHANGE,
5 | payload: {id: id, key: key, value: value}
6 | })
7 |
8 | export const argumentSort = (oldIndex, newIndex) => ({
9 | type: ARG_SORT,
10 | payload: {oldIndex: oldIndex, newIndex: newIndex}
11 | })
12 |
13 | export const argumentAdd = () => ({
14 | type: ARG_ADD
15 | })
16 |
17 | export const argumentRemove = (id) => ({
18 | type: ARG_REMOVE,
19 | payload: {id: id}
20 | })
--------------------------------------------------------------------------------
/src/js/actions/localVars.js:
--------------------------------------------------------------------------------
1 | import { LVAR_CHANGE, LVAR_SORT, LVAR_ADD, LVAR_REMOVE } from '../helpers/eventTypes'
2 |
3 | export const localVarChange = (id, key, value) => ({
4 | type: LVAR_CHANGE,
5 | payload: {id: id, key: key, value: value}
6 | })
7 |
8 | export const localVarSort = (oldIndex, newIndex) => ({
9 | type: LVAR_SORT,
10 | payload: {oldIndex: oldIndex, newIndex: newIndex}
11 | })
12 |
13 | export const localVarAdd = () => ({
14 | type: LVAR_ADD
15 | })
16 |
17 | export const localVarRemove = (id) => ({
18 | type: LVAR_REMOVE,
19 | payload: {id: id}
20 | })
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "react"
4 | ],
5 |
6 | "parserOptions": {
7 | "ecmaVersion": 6,
8 | "sourceType": "module",
9 | "ecmaFeatures": {
10 | "jsx": true,
11 | "experimentalObjectRestSpread": true
12 | }
13 | },
14 |
15 | "env": {
16 | "es6": true,
17 | "browser": true,
18 | "node": true,
19 | "mocha": true
20 | },
21 |
22 | "extends": [
23 | "eslint:recommended",
24 | "plugin:react/recommended"
25 | ],
26 |
27 | "rules": {
28 | "react/prop-types": 1,
29 | "react/no-unescaped-entities": 0,
30 | "quotes": [1, "single", { "avoidEscape": true, "allowTemplateLiterals": true }]
31 | }
32 | }
--------------------------------------------------------------------------------
/src/js/actions/options.js:
--------------------------------------------------------------------------------
1 | import { OPT_LEGACY, OPT_CHANGE } from '../helpers/eventTypes'
2 |
3 | export const legacyToggle = () => ({
4 | type: OPT_LEGACY
5 | })
6 |
7 | export const prefixChange = (id, value) => ({
8 | type: OPT_CHANGE,
9 | payload: {id: id, value: value}
10 | })
11 |
12 | export const funcTagChange = (value) => ({
13 | type: OPT_CHANGE,
14 | payload: {id: 'functionTag', value: value}
15 | })
16 |
17 | export const descTagChange = (value) => ({
18 | type: OPT_CHANGE,
19 | payload: {id: 'descriptionTag', value: value}
20 | })
21 |
22 | export const argTagChange = (value) => ({
23 | type: OPT_CHANGE,
24 | payload: {id: 'argumentTag', value: value}
25 | })
--------------------------------------------------------------------------------
/src/js/components/options/TagMenu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Grid from 'material-ui/Grid'
3 | import LocalVarPrefixField from './LocalVarPrefixField'
4 | import FuncTagSelector from './FuncTagSelector'
5 | import DescTagSelector from './DescTagSelector'
6 | import ArgTagSelector from './ArgTagSelector'
7 |
8 | const TagMenu = () => (
9 |
10 |
11 |
12 |
13 |
14 |
15 | )
16 |
17 | export default TagMenu
--------------------------------------------------------------------------------
/electron/main.js:
--------------------------------------------------------------------------------
1 | const electron = require('electron');
2 |
3 | const app = electron.app;
4 | const BrowserWindow = electron.BrowserWindow;
5 |
6 | const createWindow = () => {
7 | let window = new BrowserWindow({
8 | height: 720,
9 | width: 1280
10 | });
11 |
12 | window.loadURL(`file://${__dirname}/index.html`);
13 | window.setMenu(null);
14 |
15 | return window;
16 | };
17 |
18 | app.on('ready', () => {
19 | let mainWindow = createWindow();
20 |
21 | app.on('window-all-closed', () => {
22 | if (process.platform !== 'darwin') {
23 | app.quit();
24 | }
25 |
26 | mainWindow = null;
27 | });
28 |
29 | app.on('activate', function() {
30 | if (mainWindow === null) {
31 | createWindow();
32 | }
33 | });
34 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GML Script Wizard
2 |
3 | **[GML Script Wizard](https://mstop4.github.io/gml-script-wizard/)** is a tool that will help you generate and modify GML script headers just be filling in a few fields.
4 |
5 | 
6 | 
7 |
8 | * Conforms to both GM:S 1.4 and GMS 2 (JSDoc) documentation styles for documenting scripts.
9 | * Add, remove, and rearrange arguments and additional local variables with ease.
10 | * Copy the script template with a simple click of a button to paste it into the GameMaker Studio IDE or the script editor, or your choice.
11 |
12 | ## Demo
13 |
14 | https://mstop4.github.io/gml-script-wizard/
15 |
--------------------------------------------------------------------------------
/src/js/components/arguments/AddArgumentButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import IconButton from 'material-ui/IconButton'
4 | import Icon from 'material-ui/Icon'
5 |
6 | import { argumentAdd } from '../../actions/arguments'
7 | import PropTypes from 'prop-types'
8 |
9 | const mapDispatchToProps = (dispatch) => ({
10 | onClick: () => dispatch(argumentAdd())
11 | })
12 |
13 | const AddArgumentButton = ({ onClick }) => (
14 |
15 |
20 | add_circle
21 |
22 |
23 | )
24 |
25 | AddArgumentButton.propTypes = {
26 | onClick: PropTypes.func
27 | }
28 |
29 | export default connect(null, mapDispatchToProps)(AddArgumentButton)
--------------------------------------------------------------------------------
/src/js/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux'
2 | import rootReducer from '../reducers/index'
3 | import { loadState, saveState } from './localStorage'
4 | import { initialState } from '../helpers/initialState'
5 | import throttle from 'lodash/throttle'
6 |
7 | // TODO: Figure out how to configure Redux devtools for production/development
8 |
9 | let initialStateCopy = initialState
10 |
11 | const loadedState = loadState();
12 | if (loadedState) {
13 | initialStateCopy.options = loadedState.options
14 | }
15 |
16 | const store = createStore(
17 | rootReducer,
18 | initialStateCopy//,
19 | //window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
20 | )
21 |
22 | store.subscribe(throttle(() => {
23 | saveState({
24 | options: store.getState().options
25 | })
26 | }, 1000))
27 |
28 | export default store
--------------------------------------------------------------------------------
/src/js/components/localvars/AddLocalVarButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import IconButton from 'material-ui/IconButton'
4 | import Icon from 'material-ui/Icon'
5 |
6 | import { localVarAdd } from '../../actions/localVars'
7 | import PropTypes from 'prop-types'
8 |
9 | const mapDispatchToProps = (dispatch) => ({
10 | onClick: () => dispatch(localVarAdd())
11 | })
12 |
13 | const AddLocalVarButton = ({ onClick }) => (
14 |
15 |
20 | add_circle
21 |
22 |
23 | )
24 |
25 | AddLocalVarButton.propTypes = {
26 | onClick: PropTypes.func
27 | }
28 |
29 | export default connect(null, mapDispatchToProps)(AddLocalVarButton)
--------------------------------------------------------------------------------
/src/js/helpers/ColourTheme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from 'material-ui/styles'
2 | import red from 'material-ui/colors/red'
3 | import grey from 'material-ui/colors/grey'
4 |
5 | const ColourTheme = createMuiTheme({
6 | typography: {
7 | fontFamily: '"Open Sans",sans-serif',
8 | headline: {
9 | fontFamily: 'Oswald'
10 | }
11 | },
12 |
13 | palette: {
14 | type: 'dark',
15 | background: {
16 | paper: '#232323'
17 | },
18 |
19 | primary: {
20 | light: '#039d5b',
21 | main: '#039d5b',
22 | dark: '#00664d'
23 | },
24 | secondary: {
25 | light: grey[400],
26 | main: grey[600],
27 | dark: grey[700]
28 | },
29 | error: {
30 | light: red[400],
31 | main: red[600],
32 | dark: red[700]
33 | }
34 | }
35 | })
36 |
37 | export default ColourTheme
--------------------------------------------------------------------------------
/src/js/components/ScriptNameField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import TextField from 'material-ui/TextField'
4 | import PropTypes from 'prop-types'
5 |
6 | import { scriptNameChange } from '../actions/id'
7 |
8 | const mapStateToProps = (state) => ({
9 | value: state.scriptName
10 | })
11 |
12 | const mapDispatchToProps = (dispatch) => ({
13 | onChange: (event) => dispatch(scriptNameChange(event.target.value))
14 | })
15 |
16 | const ScriptNameField = ({ value, onChange }) => {
17 | return (
18 |
26 | )
27 | }
28 |
29 | ScriptNameField.propTypes = {
30 | value: PropTypes.string,
31 | onChange: PropTypes.func
32 | }
33 |
34 | export default connect(mapStateToProps, mapDispatchToProps)(ScriptNameField)
--------------------------------------------------------------------------------
/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CleanWebpackPlugin = require('clean-webpack-plugin')
3 | const HtmlWebPackPlugin = require('html-webpack-plugin')
4 |
5 | module.exports = {
6 | entry: ['babel-polyfill', './src/js/index.js'],
7 |
8 | output: {
9 | path: path.resolve(__dirname, 'dist'),
10 | filename: 'js/bundle.js'
11 | },
12 |
13 | module: {
14 | rules: [
15 | {
16 | test: /\.js$/,
17 | exclude: /node_modules/,
18 | use: 'babel-loader'
19 | },
20 |
21 | {
22 | test: /\.html$/,
23 | use: 'html-loader'
24 | },
25 |
26 | {
27 | test: /\.css$/,
28 | use: [ 'style-loader', 'css-loader' ]
29 | }
30 | ]
31 | },
32 |
33 | plugins: [
34 | new CleanWebpackPlugin(['dist']),
35 | new HtmlWebPackPlugin({
36 | template: './src/index.html',
37 | filename: './index.html'
38 | })
39 | ]
40 | }
--------------------------------------------------------------------------------
/src/js/components/DescriptionField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import TextField from 'material-ui/TextField'
4 | import PropTypes from 'prop-types'
5 |
6 | import { descriptionChange } from '../actions/id'
7 |
8 | const mapStateToProps = (state) => ({
9 | value: state.description
10 | })
11 |
12 | const mapDispatchToProps = (dispatch) => ({
13 | onChange: (event) => dispatch(descriptionChange(event.target.value)),
14 | })
15 |
16 | const DescriptionField = ({ value, onChange }) => {
17 | return (
18 |
27 | )
28 | }
29 |
30 | DescriptionField.propTypes = {
31 | value: PropTypes.string,
32 | onChange: PropTypes.func
33 | }
34 |
35 | export default connect(mapStateToProps, mapDispatchToProps)(DescriptionField)
--------------------------------------------------------------------------------
/src/js/components/localvars/LocalVarContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Typography from 'material-ui/Typography'
3 | import { Scrollbars } from 'react-custom-scrollbars'
4 | //import PropTypes from 'prop-types'
5 |
6 | import LocalVarSortable from './LocalVarSortable'
7 | import AddLocalVarButton from './AddLocalVarButton'
8 |
9 | const LocalVarContainer = () => {
10 | return (
11 |
12 |
13 |
14 | Local Variables
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | )
27 | }
28 |
29 | //LocalVarContainer.propTypes = {}
30 |
31 | export default LocalVarContainer
--------------------------------------------------------------------------------
/src/js/components/arguments/ArgumentContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Typography from 'material-ui/Typography'
3 | import { Scrollbars } from 'react-custom-scrollbars'
4 | //import PropTypes from 'prop-types'
5 |
6 | import ArgumentSortable from './ArgumentSortable'
7 | import AddArgumentButton from './AddArgumentButton'
8 |
9 | import '../../../styles/list-container.css'
10 |
11 | const ArgumentContainer = () => {
12 | return (
13 |
14 |
15 |
16 | Arguments
17 |
18 |
21 |
22 |
27 |
28 | )
29 | }
30 |
31 | //ArgumentContainer.propTypes = {}
32 |
33 | export default ArgumentContainer
--------------------------------------------------------------------------------
/src/js/components/help/HelpButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import HelpDialog from './HelpDialog'
3 | import IconButton from 'material-ui/IconButton'
4 | import Icon from 'material-ui/Icon'
5 |
6 | class HelpButton extends Component {
7 | constructor() {
8 | super()
9 |
10 | this.state = {
11 | dialogOpen: false
12 | }
13 |
14 | this.handleDialogOpen = this.handleDialogOpen.bind(this)
15 | this.handleDialogClose = this.handleDialogClose.bind(this)
16 | }
17 |
18 | handleDialogOpen() {
19 | this.setState({ dialogOpen: true })
20 | }
21 |
22 | handleDialogClose() {
23 | this.setState({ dialogOpen: false })
24 | }
25 |
26 | render() {
27 | return (
28 |
29 |
33 |
34 | help_outline
35 |
36 |
37 | )
38 | }
39 | }
40 |
41 | export default HelpButton
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## v.0.9.3
4 |
5 | * Added basic native builds for Windows, macOS, and Linux for offline use. I know there's no Linux IDE for either GM:S 1.4 or GMS2, I just added it in for the sake of completeness. 🙂
6 | * Argument descriptions are now added beside their respective argument declarations in GameMaker: Studio 1.4 mode.
7 | * Updated old dependencies that had security issues.
8 |
9 | ## v.0.9.2
10 |
11 | * App settings now persist between sessions, until your browser's local storage is cleared.
12 |
13 | ## v.0.9.1
14 |
15 | * Switched to using Redux to maintain app state.
16 | * Added settings to change between different function, description, and argument tags.
17 | * Bug fix: Clicking the delete button on a local variable field will now remove that field instead of always the first one.
18 |
19 | ## v.0.8
20 |
21 | Added a Settings menu with the following options:
22 |
23 | * A toggle to switch between generating GameMaker Studio 2 and GameMaker: Studio 1.4-style script documentation.
24 | * Customizable prefix for local variables.
25 |
26 | ## v.0.7
27 |
28 | * First public release.
29 |
--------------------------------------------------------------------------------
/src/js/components/OutputBox.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import Card, { CardContent } from 'material-ui/Card'
4 | import Typography from 'material-ui/Typography'
5 | import CopyScriptButton from './CopyScriptButton'
6 | import PropTypes from 'prop-types'
7 |
8 | import '../../styles/output-box.css'
9 |
10 | const mapStateToProps = (state) => ({
11 | value: state.outputValue
12 | })
13 |
14 | const OutputBox = ({ value }) => {
15 | return (
16 |
17 |
18 |
19 | Script
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {value}
29 |
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | OutputBox.propTypes = {
37 | value: PropTypes.string
38 | }
39 |
40 | export default connect(mapStateToProps, null)(OutputBox)
--------------------------------------------------------------------------------
/src/js/components/options/LocalVarPrefixField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import { prefixChange } from '../../actions/options'
4 | import TextField from 'material-ui/TextField'
5 | import propTypes from 'prop-types'
6 |
7 | const mapStateToProps = (state) => ({
8 | localVarPrefix: state.options.localVarPrefix
9 | })
10 |
11 | const mapDispatchToProps = (dispatch) => ({
12 | onPrefixChange: (event) => dispatch(prefixChange(event.target.id, event.target.value))
13 | })
14 |
15 | const LocalVarPrefixField = (props) => {
16 | let { localVarPrefix, onPrefixChange } = props
17 |
18 | const handleTextFieldClick = (event) => {
19 | event.stopPropagation()
20 | }
21 |
22 | return (
23 |
32 | )
33 | }
34 |
35 | LocalVarPrefixField.propTypes = {
36 | localVarPrefix: propTypes.string,
37 | onPrefixChange: propTypes.func
38 | }
39 |
40 | export default connect( mapStateToProps, mapDispatchToProps )(LocalVarPrefixField)
--------------------------------------------------------------------------------
/src/js/components/options/OptionsButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import OptionsDialog from './OptionsDialog'
3 | import IconButton from 'material-ui/IconButton'
4 | import Icon from 'material-ui/Icon'
5 | import PropTypes from 'prop-types'
6 |
7 | class OptionsButton extends Component {
8 | constructor(props) {
9 | super(props)
10 |
11 | this.state = {
12 | dialogOpen: false
13 | }
14 |
15 | this.handleDialogOpen = this.handleDialogOpen.bind(this)
16 | this.handleDialogClose = this.handleDialogClose.bind(this)
17 | }
18 |
19 | handleDialogOpen() {
20 | this.setState({ dialogOpen: true })
21 | }
22 |
23 | handleDialogClose() {
24 | this.setState({ dialogOpen: false })
25 | }
26 |
27 | render() {
28 | return (
29 |
30 |
34 |
35 | build
36 |
37 |
38 | )
39 | }
40 | }
41 |
42 | OptionsButton.propTypes = {
43 | options: PropTypes.object,
44 | onEvent: PropTypes.func
45 | }
46 |
47 | export default OptionsButton
--------------------------------------------------------------------------------
/src/js/electron-index.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const path = require('path');
3 | const url = require('url');
4 |
5 | // Global reference to window object to prevent it from being garbage-collected
6 | let win;
7 |
8 | function createWindow() {
9 | win = new BrowserWindow({width: 1280, height: 720});
10 |
11 | const startUrl = process.env.ELECTRON_START_URL || url.format({
12 | pathname: path.join(__dirname, './../../dist/index.html'),
13 | protocol: 'file:',
14 | slashes: true
15 | });
16 | win.loadURL(startUrl);
17 |
18 | win.webContents.openDevTools();
19 |
20 | // dereference window when it is closed
21 | win.on('closed', () => {
22 | win = null;
23 | });
24 | }
25 |
26 | app.on('ready', createWindow);
27 |
28 | app.on('window-all-closed', () => {
29 | // If current OS is not macOS, quit the app
30 | // macOS apps are able to still run after all its windows are closed
31 | if (process.platform !== 'darwin') {
32 | app.quit();
33 | }
34 | });
35 |
36 | app.on('activate', () => {
37 | // On macOS, create a window in the app when the dock icon is clicked and there
38 | // are no other windows present
39 | if (win === null) {
40 | createWindow();
41 | }
42 | });
--------------------------------------------------------------------------------
/src/js/components/options/LegacyToggle.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import { FormGroup, FormControlLabel, FormLabel } from 'material-ui/Form'
4 | import Switch from 'material-ui/Switch'
5 |
6 | import { legacyToggle } from '../../actions/options'
7 | import propTypes from 'prop-types'
8 |
9 | const mapStateToProps = (state) => ({
10 | legacyMode: state.options.legacyMode
11 | })
12 |
13 | const mapDispatchToProps = (dispatch) => ({
14 | onLegacyChange: () => dispatch(legacyToggle()),
15 | })
16 |
17 | const LegacyToggle = (props) => {
18 | let { legacyMode, onLegacyChange } = props
19 |
20 | return (
21 |
22 | Documentation Style
23 |
29 | }
30 | label={legacyMode ? 'GameMaker: Studio 1.4' : 'GameMaker Studio 2'}
31 | />
32 |
33 | )
34 | }
35 |
36 | LegacyToggle.propTypes = {
37 | legacyMode: propTypes.bool,
38 | onLegacyChange: propTypes.func
39 | }
40 |
41 | export default connect( mapStateToProps, mapDispatchToProps )(LegacyToggle)
--------------------------------------------------------------------------------
/src/js/components/TitleBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import AppBar from 'material-ui/AppBar'
3 | import Toolbar from 'material-ui/Toolbar'
4 | import Typography from 'material-ui/Typography'
5 | import Icon from 'material-ui/Icon'
6 | import HelpButton from './help/HelpButton'
7 | import OptionsButton from './options/OptionsButton'
8 | import PropTypes from 'prop-types'
9 |
10 | import '../../styles/appbar.css'
11 |
12 | const TitleBar = (props) => {
13 | let { options, onEvent } = props
14 |
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | description GML Script Wizard
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | TitleBar.propTypes = {
41 | options: PropTypes.object,
42 | onEvent: PropTypes.func
43 | }
44 |
45 | export default TitleBar
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2018, M.S.T.O.P.
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/src/js/components/options/FuncTagSelector.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import { MenuItem } from 'material-ui/Menu'
4 | import Input, { InputLabel } from 'material-ui/Input'
5 | import Select from 'material-ui/Select'
6 | import { FormControl } from 'material-ui/Form'
7 |
8 | import { funcTagChange } from '../../actions/options'
9 | import propTypes from 'prop-types'
10 |
11 | const style = {
12 | width: '10em'
13 | }
14 |
15 | const mapStateToProps = (state) => ({
16 | funcTag: state.options.functionTag
17 | })
18 |
19 | const mapDispatchToProps = (dispatch) => ({
20 | onTagChange: (event) => dispatch(funcTagChange(event.target.value)),
21 | })
22 |
23 | const FuncTagSelector = (props) => {
24 | let { funcTag, onTagChange } = props
25 |
26 | return (
27 |
28 | Function Tag
29 | }
34 | style={style}
35 | >
36 | @function
37 | @func
38 |
39 |
40 | )
41 | }
42 |
43 | FuncTagSelector.propTypes = {
44 | funcTag: propTypes.string,
45 | onTagChange: propTypes.func
46 | }
47 |
48 | export default connect( mapStateToProps, mapDispatchToProps )(FuncTagSelector)
--------------------------------------------------------------------------------
/src/js/components/options/DescTagSelector.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import { MenuItem } from 'material-ui/Menu'
4 | import Input, { InputLabel } from 'material-ui/Input'
5 | import Select from 'material-ui/Select'
6 | import { FormControl } from 'material-ui/Form'
7 |
8 | import { descTagChange } from '../../actions/options'
9 | import propTypes from 'prop-types'
10 |
11 | const style = {
12 | width: '10em'
13 | }
14 |
15 | const mapStateToProps = (state) => ({
16 | descTag: state.options.descriptionTag
17 | })
18 |
19 | const mapDispatchToProps = (dispatch) => ({
20 | onTagChange: (event) => dispatch(descTagChange(event.target.value)),
21 | })
22 |
23 | const DescTagSelector = (props) => {
24 | let { descTag, onTagChange } = props
25 |
26 | return (
27 |
28 | Description Tag
29 | }
34 | style={style}
35 | >
36 | @description
37 | @desc
38 |
39 |
40 | )
41 | }
42 |
43 | DescTagSelector.propTypes = {
44 | descTag: propTypes.string,
45 | onTagChange: propTypes.func
46 | }
47 |
48 | export default connect( mapStateToProps, mapDispatchToProps )(DescTagSelector)
--------------------------------------------------------------------------------
/src/js/components/options/ArgTagSelector.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import { MenuItem } from 'material-ui/Menu'
4 | import Input, { InputLabel } from 'material-ui/Input'
5 | import Select from 'material-ui/Select'
6 | import { FormControl } from 'material-ui/Form'
7 |
8 | import { argTagChange } from '../../actions/options'
9 | import propTypes from 'prop-types'
10 |
11 | const style = {
12 | width: '10em'
13 | }
14 |
15 | const mapStateToProps = (state) => ({
16 | argTag: state.options.argumentTag
17 | })
18 |
19 | const mapDispatchToProps = (dispatch) => ({
20 | onTagChange: (event) => dispatch(argTagChange(event.target.value)),
21 | })
22 |
23 | const ArgTagSelector = (props) => {
24 | let { argTag, onTagChange } = props
25 |
26 | return (
27 |
28 | Argument Tag
29 | }
33 | style={style}
34 | >
35 | @argument
36 | @arg
37 | @param
38 |
39 |
40 | )
41 | }
42 |
43 | ArgTagSelector.propTypes = {
44 | argTag: propTypes.string,
45 | onTagChange: propTypes.func
46 | }
47 |
48 | export default connect( mapStateToProps, mapDispatchToProps )(ArgTagSelector)
--------------------------------------------------------------------------------
/src/js/components/arguments/ArgumentField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SortableElement } from 'react-sortable-hoc'
3 |
4 | import IconButton from 'material-ui/IconButton'
5 | import Icon from 'material-ui/Icon'
6 | import TextField from 'material-ui/TextField'
7 | import Card from 'material-ui/Card'
8 | import DragHandle from '../DragHandle'
9 |
10 | import '../../../styles/list-field.css'
11 |
12 | const ArgumentField = SortableElement( (props) => {
13 | let { id, name, onChange, onOpen } = props
14 |
15 | const onClick = (event) => {
16 | event.stopPropagation()
17 | }
18 |
19 | const onFieldChange = (event) => {
20 | let newArg = event.target.value
21 | let key = event.target.id
22 | onChange(id, key, newArg)
23 | }
24 |
25 | const onDialogOpen = () => {
26 | onOpen(id);
27 | }
28 |
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
44 |
45 |
46 |
47 | mode_edit
48 |
49 |
50 |
51 |
52 | )
53 | })
54 |
55 | export default ArgumentField
--------------------------------------------------------------------------------
/src/js/components/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Grid from 'material-ui/Grid'
3 |
4 | import TitleBar from './TitleBar'
5 | import OutputBox from './OutputBox'
6 | import DescriptionField from './DescriptionField'
7 | import ArgumentContainer from './arguments/ArgumentContainer'
8 | import LocalVarContainer from './localvars/LocalVarContainer'
9 | import ScriptNameField from './ScriptNameField'
10 |
11 | import { MuiThemeProvider } from 'material-ui/styles'
12 | import ColourTheme from '../helpers/ColourTheme'
13 |
14 | import '../../styles/main.css'
15 |
16 | class App extends Component {
17 |
18 | constructor() {
19 | super()
20 |
21 | this.state = {}
22 | }
23 |
24 | render() {
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
48 | )
49 | }
50 | }
51 |
52 | export default App
--------------------------------------------------------------------------------
/src/js/components/localvars/LocalVarField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SortableElement } from 'react-sortable-hoc'
3 |
4 | import IconButton from 'material-ui/IconButton'
5 | import Icon from 'material-ui/Icon'
6 | import TextField from 'material-ui/TextField'
7 | import Card from 'material-ui/Card'
8 |
9 | import DragHandle from '../DragHandle'
10 |
11 | import '../../../styles/list-field.css'
12 |
13 | const LocalVarField = SortableElement( (props) => {
14 | let { id, name, onChange, onRemove } = props
15 |
16 | const onClick = (event) => {
17 | event.stopPropagation()
18 | }
19 |
20 | const onFieldChange = (event) => {
21 | let newArg = event.target.value
22 | let key = event.target.id
23 | onChange(id, key, newArg)
24 | }
25 |
26 | const onFieldRemove = () => {
27 | onRemove(id)
28 | }
29 |
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
45 |
46 |
47 |
52 | delete_forever
53 |
54 |
55 |
56 |
57 | )
58 | })
59 |
60 | export default LocalVarField
--------------------------------------------------------------------------------
/src/js/components/options/OptionsDialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 |
4 | import Transition from '../Transition'
5 | import LegacyToggle from './LegacyToggle'
6 | import TagMenu from './TagMenu'
7 |
8 | import Dialog from 'material-ui/Dialog/Dialog'
9 | import AppBar from 'material-ui/AppBar/AppBar'
10 | import Toolbar from 'material-ui/Toolbar/Toolbar'
11 | import Typography from 'material-ui/Typography'
12 | import IconButton from 'material-ui/IconButton'
13 | import Icon from 'material-ui/Icon'
14 | import Divider from 'material-ui/Divider/Divider'
15 |
16 | import { prefixChange } from '../../actions/options'
17 | import propTypes from 'prop-types'
18 |
19 | import '../../../styles/options.css'
20 |
21 | const mapStateToProps = (state) => ({
22 | options: {
23 | localVarPrefix: state.options.localVarPrefix
24 | }
25 | })
26 |
27 | const mapDispatchToProps = (dispatch) => ({
28 | onPrefixChange: (event) => dispatch(prefixChange(event.target.id, event.target.value))
29 | })
30 |
31 | const OptionsDialog = (props) => {
32 | let { isOpen, onClose } = props
33 |
34 | return (
35 |
42 |
43 |
44 |
47 | close
48 |
49 |
50 | Settings
51 |
52 |
53 |
54 |
59 |
60 | )
61 | }
62 |
63 | OptionsDialog.propTypes = {
64 | isOpen: propTypes.bool,
65 | options: propTypes.object,
66 | onClose: propTypes.func,
67 | onLegacyChange: propTypes.func,
68 | onPrefixChange: propTypes.func
69 | }
70 |
71 | export default connect(mapStateToProps, mapDispatchToProps)(OptionsDialog)
--------------------------------------------------------------------------------
/src/js/components/localvars/LocalVarSortable.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import LocalVarField from './LocalVarField'
4 | import { SortableContainer } from 'react-sortable-hoc'
5 |
6 | import { localVarChange, localVarSort, localVarRemove } from '../../actions/localVars'
7 | import PropTypes from 'prop-types'
8 |
9 | const LocalVarList = SortableContainer( (props) => {
10 | let { items, onChange, onOpen, onRemove } = props
11 |
12 | return (
13 |
14 |
15 | {items.map((localVar, index) => (
16 |
24 | ))}
25 |
26 |
27 | )
28 | })
29 |
30 | const mapStateToProps = (state) => ({
31 | items: state.localVars
32 | })
33 |
34 | const mapDispatchToProps = (dispatch) => ({
35 | onChange: (id, key, value) => dispatch(localVarChange(id, key, value)),
36 | onRemove: (id) => dispatch(localVarRemove(id)),
37 | onSortEnd: (event) => dispatch(localVarSort(event.oldIndex, event.newIndex))
38 | })
39 |
40 | class LocalVarSortable extends Component {
41 |
42 | constructor(props) {
43 | super(props)
44 |
45 | this.state = {
46 | dialogOpen: false,
47 | index: 0
48 | }
49 | }
50 |
51 | render() {
52 | return (
53 |
54 |
63 |
64 | )
65 | }
66 | }
67 |
68 | LocalVarSortable.propTypes = {
69 | onChange: PropTypes.func,
70 | onRemove: PropTypes.func,
71 | items: PropTypes.array,
72 | onSortEnd: PropTypes.func
73 | }
74 |
75 | export default connect(mapStateToProps, mapDispatchToProps)(LocalVarSortable)
--------------------------------------------------------------------------------
/src/js/components/CopyScriptButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import IconButton from 'material-ui/IconButton'
3 | import Icon from 'material-ui/Icon'
4 | import Snackbar from 'material-ui/Snackbar'
5 |
6 | class CopyScriptButton extends Component {
7 |
8 | constructor() {
9 | super()
10 |
11 | this.state = {
12 | snackbarOpen: false
13 | }
14 |
15 | this.copyScript = this.copyScript.bind(this)
16 | this.handleClose = this.handleClose.bind(this)
17 | }
18 |
19 | copyScript() {
20 | // Copies script text to a temporary textarea so that it can be
21 | // copied to the clipboard
22 | let scriptText = document.getElementById('generated-script').innerHTML
23 | let tempBox = document.createElement('textarea')
24 |
25 | document.body.appendChild(tempBox)
26 | tempBox.setAttribute('id', 'tempBox')
27 | scriptText = scriptText.replace(/ /g, ' ')
28 | document.getElementById('tempBox').value = scriptText
29 | tempBox.select()
30 | document.execCommand('Copy')
31 | document.body.removeChild(tempBox)
32 | this.setState({ snackbarOpen: true })
33 | }
34 |
35 | handleClose() {
36 | this.setState({ snackbarOpen: false })
37 | }
38 |
39 | render() {
40 | return (
41 |
42 |
57 | close
58 |
59 | ]}
60 | />
61 |
62 |
67 | content_copy
68 |
69 |
70 | )
71 | }
72 | }
73 |
74 | export default CopyScriptButton
--------------------------------------------------------------------------------
/src/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | import * as event from '../helpers/eventTypes'
2 | import generateScript from '../helpers/generateScript'
3 | import { arrayMove } from 'react-sortable-hoc'
4 | import { initialState } from '../helpers/initialState'
5 |
6 | const rootReducer = (state = initialState, action) => {
7 | let newState = {
8 | ...state,
9 | options: { ...state.options },
10 | args: [ ...state.args ],
11 | localVars: [ ...state.localVars ]
12 | }
13 |
14 | switch (action.type) {
15 |
16 | case event.NAME_CHANGE: {
17 | newState.scriptName = action.payload.value
18 | break
19 | }
20 |
21 | case event.DESC_CHANGE: {
22 | newState.description = action.payload.value
23 | break
24 | }
25 |
26 | case event.ARG_CHANGE: {
27 | newState.args[action.payload.id][action.payload.key] = action.payload.value
28 | break
29 | }
30 |
31 | case event.ARG_SORT: {
32 | newState.args = arrayMove(newState.args, action.payload.oldIndex, action.payload.newIndex)
33 | break
34 | }
35 |
36 | case event.ARG_ADD: {
37 | newState.args.push({name: '', type: '', description: ''})
38 | break
39 | }
40 |
41 | case event.ARG_REMOVE: {
42 | if (newState.args.length > 0) {
43 | newState.args.splice(action.payload.id, 1)
44 | }
45 | break
46 | }
47 |
48 | case event.LVAR_CHANGE: {
49 | newState.localVars[action.payload.id][action.payload.key] = action.payload.value
50 | break
51 | }
52 |
53 | case event.LVAR_SORT: {
54 | newState.localVars = arrayMove(newState.localVars, action.payload.oldIndex, action.payload.newIndex)
55 | break
56 | }
57 |
58 | case event.LVAR_ADD: {
59 | newState.localVars.push({name: '', type: '', description: ''})
60 | break
61 | }
62 |
63 | case event.LVAR_REMOVE: {
64 | if (newState.localVars.length > 0) {
65 | newState.localVars.splice(action.payload.id, 1)
66 | }
67 | break
68 | }
69 |
70 | case event.OPT_LEGACY: {
71 | newState.options.legacyMode = !newState.options.legacyMode
72 | break
73 | }
74 |
75 | case event.OPT_CHANGE: {
76 | newState.options[action.payload.id] = action.payload.value
77 | break
78 | }
79 | }
80 |
81 | newState.outputValue = generateScript(newState)
82 | return newState
83 | }
84 |
85 | export default rootReducer
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gml-script-wizard",
3 | "version": "0.9.3",
4 | "description": "GML Script Wizard",
5 | "main": "index.js",
6 | "repository": "https://github.com/mstop4/gml-script-wizard.git",
7 | "author": "mstop4 ",
8 | "license": "BSD-2-Clause",
9 | "scripts": {
10 | "start": "webpack-dev-server --port 8081 --host 0.0.0.0 --hot --open --config webpack.dev.js",
11 | "build": "./node_modules/.bin/webpack --config webpack.prod.js && cp -r dist/js electron && cp -r dist/index.html electron",
12 | "lint": "esw webpack.config.* src tools --color",
13 | "lint:watch": "yarn lint --watch",
14 | "electron": "npx electron electron/main.js",
15 | "build:electron-win": "electron-packager electron GMLScriptWizard --platform=win32 --arch=x64 --overwrite --no-tmpdir",
16 | "build:electron-mac": "electron-packager electron GMLScriptWizard --platform=darwin --arch=x64 --overwrite --no-tmpdir",
17 | "build:electron-linux": "electron-packager electron GMLScriptWizard --platform=linux --arch=x64 --overwrite --no-tmpdir",
18 | "gh-pages": "yarn build && npx push-dir --dir=dist --branch=gh-pages --cleanup"
19 | },
20 | "devDependencies": {
21 | "babel-core": "^6.26.0",
22 | "babel-loader": "^7.1.2",
23 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
24 | "babel-preset-env": "^1.6.1",
25 | "babel-preset-react": "^6.24.1",
26 | "clean-webpack-plugin": "^0.1.17",
27 | "css-loader": "^0.28.9",
28 | "electron": "^4.0.4",
29 | "electron-packager": "^13.0.1",
30 | "eslint": "^4.18.0",
31 | "eslint-plugin-react": "^7.6.1",
32 | "eslint-watch": "^3.1.3",
33 | "html-loader": "^0.5.5",
34 | "html-webpack-plugin": "^3.0.6",
35 | "inline-source-map": "^0.6.2",
36 | "path": "^0.12.7",
37 | "style-loader": "^0.20.2",
38 | "uglifyjs-webpack-plugin": "^1.1.6",
39 | "webpack": "^4.28.3",
40 | "webpack-cli": "^3.2.0",
41 | "webpack-dev-server": "^3.1.14",
42 | "webpack-merge": "^4.1.1"
43 | },
44 | "dependencies": {
45 | "autosuggest-highlight": "^3.1.1",
46 | "babel-polyfill": "^6.26.0",
47 | "lodash": "^4.17.13",
48 | "material-ui": "^1.0.0-beta.36",
49 | "prop-types": "^15.6.0",
50 | "react": "^16.2.0",
51 | "react-autosuggest": "^9.3.3",
52 | "react-custom-scrollbars": "^4.2.1",
53 | "react-dom": "^16.2.0",
54 | "react-redux": "^5.0.7",
55 | "react-sortable-hoc": "^0.6.8",
56 | "redux": "^3.7.2"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/js/components/arguments/ArgumentSortable.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import ArgumentField from './ArgumentField'
4 | import ArgumentDialog from './ArgumentDialog'
5 | import { SortableContainer } from 'react-sortable-hoc'
6 |
7 | import { argumentChange, argumentSort, argumentRemove } from '../../actions/arguments'
8 | import PropTypes from 'prop-types'
9 |
10 | const ArgumentList = SortableContainer( (props) => {
11 | let { items, onChange, onOpen, onRemove } = props
12 |
13 | return (
14 |
15 |
16 | {items.map((arg, index) => (
17 |
25 | ))}
26 |
27 |
28 | )
29 | })
30 |
31 | const mapStateToProps = (state) => ({
32 | items: state.args
33 | })
34 |
35 | const mapDispatchToProps = (dispatch) => ({
36 | onChange: (id, key, value) => dispatch(argumentChange(id, key, value)),
37 | onRemove: (id) => dispatch(argumentRemove(id)),
38 | onSortEnd: (event) => dispatch(argumentSort(event.oldIndex, event.newIndex))
39 | })
40 |
41 | class ArgumentSortable extends Component {
42 |
43 | constructor(props) {
44 | super(props)
45 |
46 | this.state = {
47 | dialogOpen: false,
48 | index: 0
49 | }
50 |
51 | this.handleDialogOpen = this.handleDialogOpen.bind(this)
52 | this.handleDialogClose = this.handleDialogClose.bind(this)
53 | this.onFieldRemove = this.onFieldRemove.bind(this)
54 | this.onFieldChange = this.onFieldChange.bind(this)
55 | }
56 |
57 | handleDialogOpen(id) {
58 | this.setState({ dialogOpen: true, index: id })
59 | }
60 |
61 | handleDialogClose() {
62 | this.setState({ dialogOpen: false })
63 | }
64 |
65 | onFieldChange(event) {
66 | let newArg = event.target.value
67 | let key = event.target.id
68 | this.props.onChange(this.state.index, key, newArg)
69 | }
70 |
71 | onFieldRemove() {
72 | this.props.onRemove(this.state.index)
73 | this.handleDialogClose()
74 | }
75 |
76 | render() {
77 |
78 | let dialogArg
79 |
80 | if (this.props.items.length > 0) {
81 | dialogArg = this.props.items[this.state.index]
82 | } else {
83 | dialogArg = {name: '', type: '', description: ''}
84 | }
85 |
86 | return (
87 |
105 | )
106 | }
107 | }
108 |
109 | ArgumentSortable.propTypes = {
110 | onChange: PropTypes.func,
111 | onRemove: PropTypes.func,
112 | items: PropTypes.array,
113 | onSortEnd: PropTypes.func
114 | }
115 |
116 | export default connect(mapStateToProps, mapDispatchToProps)(ArgumentSortable)
--------------------------------------------------------------------------------
/src/js/components/help/HelpDialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Dialog, {DialogTitle, DialogContent, DialogActions} from 'material-ui/Dialog'
3 | import IconButton from 'material-ui/IconButton'
4 | import Icon from 'material-ui/Icon'
5 | import Transition from '../Transition'
6 | import Typography from 'material-ui/Typography/Typography'
7 | import Divider from 'material-ui/Divider'
8 | import PropTypes from 'prop-types'
9 |
10 | import '../../../styles/help.css'
11 |
12 | const HelpDialog = (props) => {
13 | let { isOpen, onClose } = props
14 |
15 | return (
16 |
23 | Help
24 |
25 | About
26 |
27 | GML Script Wizard is a tool that automatically generates a boilerplate header you can
28 | use as your script's starting point.
29 |
30 |
31 | Usage
32 | Adding Items
33 |
34 | Click on the respective add_circle at the top of each list to add a new item, then
35 | click on the text field to give the item a name.
36 |
37 |
38 | Adding Details (Arguments only)
39 |
40 | Click on mode_edit to open up the argument details dialog. Here you can specify a type and
41 | add a description for your argument. NOTE: These will only be visible in GMS2 if you
42 | have turned this feature in the settings.
43 |
44 |
45 | Removing Items
46 |
47 | For arguments, click on mode_edit to open up the argument details dialog, then click on delete_forever to remove.
48 | For local variables, click on delete_forever to remove.
49 |
50 |
51 | Reordering Items
52 |
53 | Click and drag from an item's reorder to move and reorder it. Release to put it in its new position.
54 |
55 |
56 | Copying the Script
57 |
58 | Click on content_copy to copy the generated script to the clipboard. The script can then be pasted into
59 | the script editor in GMS2.
60 |
61 |
62 |
63 |
64 |
65 | Created by:
66 | M.S.T.O.P.
67 | (@QuadolorGames )
68 | View source code on GitHub
69 |
70 |
71 |
72 |
73 |
78 | done
79 |
80 |
81 |
82 | )
83 | }
84 |
85 | HelpDialog.propTypes = {
86 | isOpen: PropTypes.bool,
87 | onClose: PropTypes.func
88 | }
89 |
90 | export default HelpDialog
--------------------------------------------------------------------------------
/src/js/helpers/generateScript.js:
--------------------------------------------------------------------------------
1 | const generateScript = ({ scriptName, description, args, localVars, options }) => {
2 |
3 | let newOutput = ''
4 |
5 | let headFunction = ''
6 | let headDescription = ''
7 | let headArgumentTypes = []
8 | let headArgumentNames = []
9 | let headArgumentDescs = []
10 |
11 | let declArguments = []
12 | let declLocals = ''
13 |
14 | // Determine how much padding is need between tags and description based on which
15 | // tags are used
16 | let funcTagLength = scriptName !== '' ? options.functionTag.length : 0
17 | let descTagLength = description !== '' ? options.descriptionTag.length : 0
18 | let argTagLength = args.length > 0 ? options.argumentTag.length : 0
19 |
20 | let tagPadLength = Math.max(funcTagLength, descTagLength, argTagLength) + 2
21 |
22 | if (options.legacyMode) {
23 | headFunction = '/// '
24 | headDescription = `//\xa0\xa0${description}`
25 | } else {
26 | headFunction = `/// ${options.functionTag}${'\xa0'.repeat(Math.max(2,tagPadLength-funcTagLength))}`
27 | headDescription = `/// ${options.descriptionTag}${'\xa0'.repeat(Math.max(2,tagPadLength-descTagLength))}${description}`
28 | }
29 |
30 | // Create script JSDoc header
31 |
32 | // Script name
33 | if (scriptName !== '') {
34 | let hasArgs = false
35 | headFunction += `${scriptName}(`
36 |
37 | for (let i = 0; i < args.length; i++) {
38 | if (args[i].name !== '') {
39 | headFunction += `${args[i].name}, `
40 | hasArgs = true
41 | }
42 | }
43 |
44 | // Strip trailing comma
45 | if (hasArgs) {
46 | headFunction = headFunction.slice(0,headFunction.length-2)
47 | }
48 |
49 | headFunction += ')'
50 | }
51 |
52 | // Arguments
53 | let currentArgIndex = 0
54 |
55 | // find the length of longest type and argument names for spacing purposes
56 | let typeMaxLength = -3
57 | let nameMaxLength = 0
58 |
59 | for (let i = 0; i < args.length; i++) {
60 | if (args[i].type && args[i].type.length > typeMaxLength) {
61 | typeMaxLength = args[i].type.length
62 | }
63 |
64 | if (args[i].name && args[i].name.length > nameMaxLength) {
65 | nameMaxLength = args[i].name.length
66 | }
67 | }
68 |
69 | for (let i = 0; i < args.length; i++) {
70 |
71 | // Build JSDoc line
72 | if (args[i].name !== '') {
73 |
74 | if (!options.legacyMode) {
75 | if (args[i].type !== '') {
76 | let spaceBufferSize = Math.max(0,typeMaxLength-args[i].type.length)
77 | headArgumentTypes.push(` {${args[i].type}}${'\xa0'.repeat(spaceBufferSize)}`)
78 | } else {
79 | headArgumentTypes.push('\xa0'.repeat(typeMaxLength+3))
80 | }
81 |
82 | if (args[i].name !== '') {
83 | let spaceBufferSize = Math.max(0,nameMaxLength-args[i].name.length)
84 | headArgumentNames.push(`${args[i].name}${'\xa0'.repeat(spaceBufferSize)}`)
85 | } else {
86 | headArgumentNames.push('\xa0'.repeat(nameMaxLength))
87 | }
88 |
89 | if (args[i].description !== '') {
90 | headArgumentDescs.push(` ${args[i].description}`)
91 | } else {
92 | headArgumentDescs.push('')
93 | }
94 | }
95 |
96 | // Build declaration line
97 | let declArg = `var ${options.localVarPrefix}${args[i].name} = argument[${currentArgIndex}];`
98 | const declArgPad = nameMaxLength - args[i].name.length + 2
99 |
100 | if (options.legacyMode && args[i].description) {
101 | declArg += `${'\xa0'.repeat(declArgPad)}// ${args[i].description}`
102 | }
103 |
104 | declArguments.push(`${declArg}\n`)
105 | currentArgIndex++
106 | }
107 | }
108 |
109 | // Additional local variables
110 | for (let i = 0; i < localVars.length; i++) {
111 | if (localVars[i].name !== '') {
112 | declLocals += `${options.localVarPrefix}${localVars[i].name}, `
113 | }
114 | }
115 |
116 | // Strip trailing comma
117 | if (localVars.length > 0) {
118 | declLocals = declLocals.slice(0,declLocals.length-2)
119 | }
120 |
121 | // Build Script
122 | // ------------
123 |
124 | let firstLine = true
125 |
126 | // @function
127 | if (scriptName !== '') {
128 | newOutput += `${headFunction}\n`
129 | firstLine = false
130 | }
131 |
132 | // @description
133 | if (description !== '') {
134 | newOutput += `${headDescription}\n`
135 | firstLine = false
136 | }
137 |
138 | // @param
139 | if (!options.legacyMode) {
140 | for (let i = 0; i < headArgumentNames.length; i++) {
141 | newOutput += `/// ${options.argumentTag}${'\xa0'.repeat(tagPadLength-argTagLength-1)}${headArgumentTypes[i]} ${headArgumentNames[i]} ${headArgumentDescs[i]}\n`
142 | }
143 |
144 | if (headArgumentNames.length > 0) {
145 | firstLine = false
146 | }
147 | }
148 |
149 | if (declArguments.length > 0 && !firstLine) {
150 | newOutput += '\n'
151 | }
152 |
153 | // argument declarations
154 | for (let i = 0; i < declArguments.length; i++) {
155 | newOutput += declArguments[i]
156 | firstLine = false
157 | }
158 |
159 | // local var declarations
160 | if (declLocals !== '') {
161 | if (!firstLine) {
162 | newOutput += '\n'
163 | }
164 |
165 | newOutput += `var ${declLocals};\n`
166 | firstLine = false
167 | }
168 |
169 | if (newOutput !== '' && !firstLine) {
170 | newOutput += '\n'
171 | }
172 |
173 | newOutput += '/* Script body goes here */'
174 |
175 | return newOutput
176 | }
177 |
178 | export default generateScript
--------------------------------------------------------------------------------
/src/js/components/arguments/ArgumentDialog.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Dialog, {DialogTitle, DialogContent, DialogActions} from 'material-ui/Dialog'
3 | import IconButton from 'material-ui/IconButton'
4 | import Icon from 'material-ui/Icon'
5 | import TextField from 'material-ui/TextField'
6 | import { MenuItem } from 'material-ui/Menu'
7 | import Paper from 'material-ui/Paper'
8 | import Transition from '../Transition'
9 | import PropTypes from 'prop-types'
10 |
11 | import Autosuggest from 'react-autosuggest'
12 | import match from 'autosuggest-highlight/match'
13 | import parse from 'autosuggest-highlight/parse'
14 |
15 | const suggestions = [
16 | { label: 'real' },
17 | { label: 'string' },
18 | { label: 'boolean' },
19 | { label: 'array' },
20 | { label: 'pointer' },
21 | { label: 'enum' },
22 | { label: 'matrix' },
23 | { label: 'list' },
24 | { label: 'queue' },
25 | { label: 'grid' },
26 | { label: 'priority' },
27 | { label: 'stack' },
28 | { label: 'map' },
29 | { label: 'surface' },
30 | { label: 'buffer' },
31 | { label: 'object' }
32 | ]
33 |
34 | function renderInput(inputProps) {
35 | const { ref, ...other} = inputProps
36 |
37 | return (
38 |
46 | )
47 | }
48 |
49 | function renderSuggestion(suggestion, { query, isHighlighted }) {
50 | const matches = match(suggestion.label, query)
51 | const parts = parse(suggestion.label, matches)
52 |
53 | return (
54 |
55 |
56 | {parts.map((part, index) => {
57 | return part.highlight ? (
58 |
59 | {part.text}
60 |
61 | ) : (
62 |
63 | {part.text}
64 |
65 | )
66 | })}
67 |
68 |
69 | )
70 | }
71 |
72 | function renderSuggestionsContainer(options) {
73 | const { containerProps, children } = options
74 |
75 | return (
76 |
77 | {children}
78 |
79 | )
80 | }
81 |
82 | function getSuggestionValue(suggestion) {
83 | return suggestion.label
84 | }
85 |
86 | function getSuggestions(value) {
87 | const inputValue = value.trim().toLowerCase()
88 | const inputLength = inputValue.length
89 | let count = 0
90 |
91 | return inputLength === 0
92 | ? []
93 | : suggestions.filter(suggestion => {
94 | const keep =
95 | count < 5 && suggestion.label.toLowerCase().slice(0, inputLength) === inputValue
96 |
97 | if (keep) {
98 | count++
99 | }
100 |
101 | return keep
102 | })
103 | }
104 |
105 | class ArgumentDialog extends Component {
106 |
107 | constructor(props) {
108 | super(props)
109 |
110 | this.state = {
111 | value: '',
112 | suggestions: []
113 | }
114 |
115 | this.handleSuggestionsFetchRequested = this.handleSuggestionsFetchRequested.bind(this)
116 | this.handleSuggestionsClearRequested = this.handleSuggestionsClearRequested.bind(this)
117 | this.handleGetSuggestion = this.handleGetSuggestion.bind(this)
118 | this.handleSumbit = this.handleSubmit.bind(this)
119 | }
120 |
121 | handleSuggestionsFetchRequested({ value }) {
122 | this.setState({
123 | suggestions: getSuggestions(value)
124 | })
125 | }
126 |
127 | handleSuggestionsClearRequested() {
128 | this.setState({
129 | suggestions: []
130 | })
131 | }
132 |
133 | handleGetSuggestion(suggestion) {
134 | let newValue = getSuggestionValue(suggestion)
135 | this.props.onChange({
136 | target: {
137 | value: newValue,
138 | id: 'type'
139 | }})
140 | return newValue
141 | }
142 |
143 | handleSubmit(event) {
144 | event.preventDefault()
145 | }
146 |
147 | render() {
148 |
149 | // Check if argInfo exists before displaying its info in the dialog
150 | let displayInfo = this.props.argInfo ? this.props.argInfo : {name: '', type: '', description: ''}
151 |
152 | return (
153 |
160 | {displayInfo.name} Details
161 |
162 |
179 |
186 |
187 |
188 |
193 | delete_forever
194 |
195 |
200 | done
201 |
202 |
203 |
204 | )
205 | }
206 | }
207 |
208 | ArgumentDialog.propTypes = {
209 | onChange: PropTypes.func,
210 | argInfo: PropTypes.object,
211 | isOpen: PropTypes.bool,
212 | onClose: PropTypes.func,
213 | onRemove: PropTypes.func
214 | }
215 |
216 | export default ArgumentDialog
--------------------------------------------------------------------------------